瀏覽代碼

Merge pull request #5754 from mailcow/feat/idp-ldap2

[Nightly] add LDAP as direct IdP
Patrick Schult 1 年之前
父節點
當前提交
9561526f33
共有 100 個文件被更改,包括 2995 次插入1765 次删除
  1. 1 0
      .gitignore
  2. 1 1
      data/Dockerfiles/sogo/bootstrap-sogo.sh
  3. 10 7
      data/conf/phpfpm/crons/keycloak-sync.php
  4. 176 0
      data/conf/phpfpm/crons/ldap-sync.php
  5. 47 0
      data/conf/sogo/custom-sogo.js
  6. 34 28
      data/conf/sogo/plist_ldap.sh
  7. 194 61
      data/web/inc/functions.auth.inc.php
  8. 186 89
      data/web/inc/functions.inc.php
  9. 5 3
      data/web/inc/functions.mailbox.inc.php
  10. 1 1
      data/web/inc/init_db.inc.php
  11. 1 1
      data/web/inc/lib/composer.json
  12. 230 110
      data/web/inc/lib/composer.lock
  13. 15 2
      data/web/inc/lib/vendor/autoload.php
  14. 34 12
      data/web/inc/lib/vendor/bin/carbon
  15. 34 12
      data/web/inc/lib/vendor/bin/var-dump-server
  16. 21 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/LICENSE
  17. 14 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/README.md
  18. 36 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/composer.json
  19. 16 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php
  20. 9 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php
  21. 9 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php
  22. 50 42
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php
  23. 1 8
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
  24. 12 4
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php
  25. 24 0
      data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php
  26. 72 65
      data/web/inc/lib/vendor/composer/ClassLoader.php
  27. 12 5
      data/web/inc/lib/vendor/composer/InstalledVersions.php
  28. 1 0
      data/web/inc/lib/vendor/composer/autoload_classmap.php
  29. 2 2
      data/web/inc/lib/vendor/composer/autoload_files.php
  30. 2 0
      data/web/inc/lib/vendor/composer/autoload_psr4.php
  31. 10 17
      data/web/inc/lib/vendor/composer/autoload_real.php
  32. 13 2
      data/web/inc/lib/vendor/composer/autoload_static.php
  33. 245 119
      data/web/inc/lib/vendor/composer/installed.json
  34. 56 32
      data/web/inc/lib/vendor/composer/installed.php
  35. 4 1
      data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/bug_report.md
  36. 4 1
      data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md
  37. 62 0
      data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml
  38. 5 42
      data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml
  39. 0 7
      data/web/inc/lib/vendor/directorytree/ldaprecord/.styleci.yml
  40. 4 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json
  41. 35 0
      data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml
  42. 5 2
      data/web/inc/lib/vendor/directorytree/ldaprecord/phpunit.xml
  43. 0 15
      data/web/inc/lib/vendor/directorytree/ldaprecord/psalm.xml
  44. 2 2
      data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md
  45. 3 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php
  46. 18 25
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php
  47. 9 13
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php
  48. 2 2
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php
  49. 49 30
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php
  50. 10 18
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php
  51. 5 8
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php
  52. 3 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php
  53. 7 12
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php
  54. 3 4
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php
  55. 1 1
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php
  56. 18 27
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php
  57. 13 20
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php
  58. 6 11
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php
  59. 14 21
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php
  60. 7 12
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php
  61. 12 21
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php
  62. 89 113
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php
  63. 3 5
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php
  64. 3 4
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php
  65. 69 33
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php
  66. 2 4
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php
  67. 2 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php
  68. 3 5
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php
  69. 2 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php
  70. 37 7
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php
  71. 30 37
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php
  72. 18 34
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php
  73. 18 28
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php
  74. 4 5
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php
  75. 6 9
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php
  76. 4 8
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php
  77. 48 58
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Password.php
  78. 3 5
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php
  79. 23 33
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php
  80. 15 23
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php
  81. 30 29
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php
  82. 13 19
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php
  83. 22 15
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php
  84. 3 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php
  85. 162 114
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php
  86. 36 8
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php
  87. 5 7
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php
  88. 12 15
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php
  89. 33 12
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php
  90. 1 0
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php
  91. 8 12
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php
  92. 47 0
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php
  93. 143 0
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php
  94. 1 2
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php
  95. 2 1
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php
  96. 1 1
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php
  97. 3 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php
  98. 1 2
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php
  99. 2 3
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php
  100. 216 175
      data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php

+ 1 - 0
.gitignore

@@ -44,6 +44,7 @@ data/conf/rspamd/local.d/*
 data/conf/rspamd/override.d/*
 data/conf/sogo/custom-theme.js
 data/conf/sogo/plist_ldap
+data/conf/sogo/plist_ldap.sh
 data/conf/sogo/sieve.creds
 data/conf/sogo/sogo-full.svg
 data/gitea/

+ 1 - 1
data/Dockerfiles/sogo/bootstrap-sogo.sh

@@ -107,7 +107,7 @@ while read -r line gal
                 </dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
   # Generate alternative LDAP authentication dict, when SQL authentication fails
   # This will nevertheless read attributes from LDAP
-  line=${line} envsubst < /etc/sogo/plist_ldap >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
+  /etc/sogo/plist_ldap.sh ${line} ${gal} >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
   echo "            </array>
         </dict>" >> /var/lib/sogo/GNUstep/Defaults/sogod.plist
 done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)

+ 10 - 7
data/conf/phpfpm/crons/keycloak-sync.php

@@ -18,7 +18,7 @@ try {
   $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
 }
 catch (PDOException $e) {
-  logMsg("danger", $e->getMessage());
+  logMsg("err", $e->getMessage());
   session_destroy();
   exit;
 }
@@ -68,11 +68,12 @@ $_SESSION['acl']['ratelimit'] = "1";
 $_SESSION['acl']['sogo_access'] = "1";
 $_SESSION['acl']['protocol_access'] = "1";
 $_SESSION['acl']['mailbox_relayhost'] = "1";
+$_SESSION['acl']['unlimited_quota'] = "1";
 
 // Init Keycloak Provider
 $iam_provider = identity_provider('init');
 $iam_settings = identity_provider('get');
-if (intval($iam_settings['periodic_sync']) != 1 && $iam_settings['import_users'] != 1) {
+if ($iam_settings['authsource'] != "keycloak" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
   session_destroy();
   exit;
 }
@@ -127,18 +128,18 @@ while (true) {
   curl_close($ch);
   
   if ($code != 200){
-    logMsg("danger", "Recieved HTTP {$code}");
+    logMsg("err", "Recieved HTTP {$code}");
     session_destroy();
     exit;
   }
   try {
     $response = json_decode($response, true);
   } catch (Exception $e) {
-    logMsg("danger", $e->getMessage());
+    logMsg("err", $e->getMessage());
     break;
   }
   if (!is_array($response)){
-    logMsg("danger", "Recieved malformed response from keycloak api");
+    logMsg("err", "Recieved malformed response from keycloak api");
     break;
   }
   if (count($response) == 0) {
@@ -181,7 +182,7 @@ while (true) {
       }
     }
     if (!$mbox_template){
-      logMsg("warning", "No matching mapper found for mailbox_template");
+      logMsg("warning", "No matching attribute mapping found for user " . $user['email']);
       continue;
     }
 
@@ -191,14 +192,16 @@ while (true) {
       mailbox('add', 'mailbox_from_template', array(
         'domain' => explode('@', $user['email'])[1],
         'local_part' => explode('@', $user['email'])[0],
+        'name' => $user['firstName'] . " " . $user['lastName'],
         'authsource' => 'keycloak',
         'template' => $mbox_template
       ));
-    } else if ($row) {
+    } else if ($row && intval($iam_settings['periodic_sync']) == 1) {
       // mailbox user does exist, sync attribtues...
       logMsg("info", "Syncing attributes for user " . $user['email']);
       mailbox('edit', 'mailbox_from_template', array(
         'username' => $user['email'],
+        'name' => $user['firstName'] . " " . $user['lastName'],
         'template' => $mbox_template
       ));
     } else {

+ 176 - 0
data/conf/phpfpm/crons/ldap-sync.php

@@ -0,0 +1,176 @@
+<?php
+
+require_once(__DIR__ . '/../web/inc/vars.inc.php');
+if (file_exists(__DIR__ . '/../web/inc/vars.local.inc.php')) {
+  include_once(__DIR__ . '/../web/inc/vars.local.inc.php');
+}
+require_once __DIR__ . '/../web/inc/lib/vendor/autoload.php';
+
+// Init database
+//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
+$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
+$opt = [
+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+    PDO::ATTR_EMULATE_PREPARES   => false,
+];
+try {
+  $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+}
+catch (PDOException $e) {
+  logMsg("err", $e->getMessage());
+  session_destroy();
+  exit;
+}
+
+// Init Redis
+$redis = new Redis();
+try {
+  if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
+    $redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
+  }
+  else {
+    $redis->connect('redis-mailcow', 6379);
+  }
+}
+catch (Exception $e) {
+  echo "Exiting: " . $e->getMessage();
+  session_destroy();
+  exit;
+}
+
+function logMsg($priority, $message, $task = "LDAP Sync") {
+  global $redis;
+
+  $finalMsg = array(
+    "time" => time(),
+    "priority" => $priority,
+    "task" => $task,
+    "message" => $message
+  );
+  $redis->lPush('CRON_LOG', json_encode($finalMsg));
+}
+
+// Load core functions first
+require_once __DIR__ . '/../web/inc/functions.inc.php';
+require_once __DIR__ . '/../web/inc/functions.auth.inc.php';
+require_once __DIR__ . '/../web/inc/sessions.inc.php';
+require_once __DIR__ . '/../web/inc/functions.mailbox.inc.php';
+require_once __DIR__ . '/../web/inc/functions.ratelimit.inc.php';
+require_once __DIR__ . '/../web/inc/functions.acl.inc.php';
+
+$_SESSION['mailcow_cc_username'] = "admin";
+$_SESSION['mailcow_cc_role'] = "admin";
+$_SESSION['acl']['tls_policy'] = "1";
+$_SESSION['acl']['quarantine_notification'] = "1";
+$_SESSION['acl']['quarantine_category'] = "1";
+$_SESSION['acl']['ratelimit'] = "1";
+$_SESSION['acl']['sogo_access'] = "1";
+$_SESSION['acl']['protocol_access'] = "1";
+$_SESSION['acl']['mailbox_relayhost'] = "1";
+$_SESSION['acl']['unlimited_quota'] = "1";
+
+// Init Provider
+$iam_provider = identity_provider('init');
+$iam_settings = identity_provider('get');
+if ($iam_settings['authsource'] != "ldap" || (intval($iam_settings['periodic_sync']) != 1 && intval($iam_settings['import_users']) != 1)) {
+  session_destroy();
+  exit;
+}
+
+// Set pagination variables
+$start = 0;
+$max = 25;
+
+// lock sync if already running
+$lock_file = '/tmp/iam-sync.lock';
+if (file_exists($lock_file)) {
+  $lock_file_parts = explode("\n", file_get_contents($lock_file));
+  $pid = $lock_file_parts[0];
+  if (count($lock_file_parts) > 1){
+    $last_execution = $lock_file_parts[1];
+    $elapsed_time = (time() - $last_execution) / 60;
+    if ($elapsed_time < intval($iam_settings['sync_interval'])) {
+      logMsg("warning", "Sync not ready (".number_format((float)$elapsed_time, 2, '.', '')."min / ".$iam_settings['sync_interval']."min)");
+      session_destroy();
+      exit;
+    }
+  }
+
+  if (posix_kill($pid, 0)) {
+    logMsg("warning", "Sync is already running");
+    session_destroy();
+    exit;
+  } else {
+    unlink($lock_file);
+  }
+}
+$lock_file_handle = fopen($lock_file, 'w');
+fwrite($lock_file_handle, getmypid());
+fclose($lock_file_handle);
+
+// Get ldap users
+$response = $iam_provider->query()
+  ->where($iam_settings['username_field'], "*")
+  ->where($iam_settings['attribute_field'], "*")
+  ->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname'])
+  ->paginate($max);
+
+// Process the users
+foreach ($response as $user) {
+  $mailcow_template = $user[$iam_settings['attribute_field']][0];
+
+  // try get mailbox user
+  $stmt = $pdo->prepare("SELECT `mailbox`.* FROM `mailbox`
+  INNER JOIN domain on mailbox.domain = domain.domain
+  WHERE `kind` NOT REGEXP 'location|thing|group'
+    AND `domain`.`active`='1'
+    AND `username` = :user");
+  $stmt->execute(array(':user' => $user[$iam_settings['username_field']][0]));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+  // check if matching attribute mapping exists
+  $mbox_template = null;
+  foreach ($iam_settings['mappers'] as $index => $mapper){
+    if ($mapper ==  $mailcow_template) {
+      $mbox_template = $iam_settings['templates'][$index];
+      break;
+    }
+  }
+  if (!$mbox_template){
+    logMsg("warning", "No matching attribute mapping found for user " . $user[$iam_settings['username_field']][0]);
+    continue;
+  }
+
+  if (!$row && intval($iam_settings['import_users']) == 1){
+    // mailbox user does not exist, create...
+    logMsg("info", "Creating user " .  $user[$iam_settings['username_field']][0]);
+    mailbox('add', 'mailbox_from_template', array(
+      'domain' => explode('@',  $user[$iam_settings['username_field']][0])[1],
+      'local_part' => explode('@',  $user[$iam_settings['username_field']][0])[0],
+      'name' => $user['displayname'][0],
+      'authsource' => 'ldap',
+      'template' => $mbox_template
+    ));
+  } else if ($row && intval($iam_settings['periodic_sync']) == 1) {
+    // mailbox user does exist, sync attribtues...
+    logMsg("info", "Syncing attributes for user " . $user[$iam_settings['username_field']][0]);
+    mailbox('edit', 'mailbox_from_template', array(
+      'username' =>  $user[$iam_settings['username_field']][0],
+      'name' => $user['displayname'][0],
+      'template' => $mbox_template
+    ));
+  } else {
+    // skip mailbox user
+    logMsg("info", "Skipping user " .  $user[$iam_settings['username_field']][0]);
+  }
+
+  sleep(0.025);
+}
+
+logMsg("info", "DONE!");
+// add last execution time to lock file
+$lock_file_handle = fopen($lock_file, 'w');
+fwrite($lock_file_handle, getmypid() . "\n" . time());
+fclose($lock_file_handle);
+session_destroy();

+ 47 - 0
data/conf/sogo/custom-sogo.js

@@ -1,3 +1,49 @@
+// redirect to mailcow login form
+document.addEventListener('DOMContentLoaded', function () {
+    var loginForm = document.forms.namedItem("loginForm");
+    if (loginForm) {
+        window.location.href = '/';
+    }
+
+    angularReady = false;
+    // Wait for the Angular components to be initialized
+    function waitForAngularComponents(callback) {
+        const targetNode = document.body;
+
+        const observer = new MutationObserver(function(mutations) {
+            mutations.forEach(function(mutation) {
+            if (mutation.addedNodes.length > 0) {
+                const toolbarElement = document.body.querySelector('md-toolbar');
+                if (toolbarElement) {
+                observer.disconnect();
+                callback();
+                }
+            }
+            });
+        });
+
+        const config = { childList: true, subtree: true };
+        observer.observe(targetNode, config);
+    }
+
+    // Usage
+    waitForAngularComponents(function() {
+        if (!angularReady){
+            angularReady = true;
+
+            const toolbarElement = document.body.querySelector('.md-toolbar-tools.sg-toolbar-group-last.layout-align-end-center.layout-row');
+
+            var htmlCode = '<a class="md-icon-button md-button md-ink-ripple" aria-label="mailcow" href="/user" aria-hidden="false" tabindex="-1">' +
+                '<md-icon class="material-icons" role="img" aria-label="build">build</md-icon>' +
+                '</a><a class="md-icon-button md-button md-ink-ripple" aria-label="mailcow" href="#" onclick="logout.submit()" aria-hidden="false" tabindex="-1">' +
+                '<md-icon class="material-icons" role="img" aria-label="settings_power">settings_power</md-icon>' +
+                '</a><form action="/" method="post" id="logout"><input type="hidden" name="logout"></form>';
+
+            toolbarElement.insertAdjacentHTML('beforeend', htmlCode);
+        }
+    });
+});
+
 // Custom SOGo JS
 
 // Change the visible font-size in the editor, this does not change the font of a html message by default
@@ -5,3 +51,4 @@ CKEDITOR.addCss("body {font-size: 16px !important}");
 
 // Enable scayt by default
 //CKEDITOR.config.scayt_autoStartup = true;
+

+ 34 - 28
data/conf/sogo/plist_ldap → data/conf/sogo/plist_ldap.sh

@@ -1,28 +1,34 @@
-                <!--
-                <example>
-                    <key>canAuthenticate</key>
-                    <string>YES</string>
-                    <key>id</key>
-                    <string>${line}_ldap</string>
-                    <key>isAddressBook</key>
-                    <string>NO</string>
-                    <key>IDFieldName</key>
-                    <string>mail</string>
-                    <key>UIDFieldName</key>
-                    <string>uid</string>
-                    <key>bindFields</key>
-                    <array>
-                        <string>mail</string>
-                    </array>
-                    <key>type</key>
-                    <string>ldap</string>
-                    <key>bindDN</key>
-                    <string>cn=admin,dc=example,dc=local</string>
-                    <key>bindPassword</key>
-                    <string>password</string>
-                    <key>baseDN</key>
-                    <string>ou=People,dc=example,dc=local</string>
-                    <key>hostname</key>
-                    <string>ldap://1.2.3.4:389</string>
-                </example>
-                -->
+#!/bin/bash
+
+domain="$1"
+gal_status="$2"
+
+echo '
+                <!--
+                <example>
+                    <key>canAuthenticate</key>
+                    <string>YES</string>
+                    <key>id</key>
+                    <string>'"${domain}_ldap"'</string>
+                    <key>isAddressBook</key>
+                    <string>'"${gal_status}"'</string>
+                    <key>IDFieldName</key>
+                    <string>mail</string>
+                    <key>UIDFieldName</key>
+                    <string>uid</string>
+                    <key>bindFields</key>
+                    <array>
+                        <string>mail</string>
+                    </array>
+                    <key>type</key>
+                    <string>ldap</string>
+                    <key>bindDN</key>
+                    <string>cn=admin,dc=example,dc=local</string>
+                    <key>bindPassword</key>
+                    <string>password</string>
+                    <key>baseDN</key>
+                    <string>ou=People,dc=example,dc=local</string>
+                    <key>hostname</key>
+                    <string>ldap://1.2.3.4:389</string>
+                </example>
+                -->'

+ 194 - 61
data/web/inc/functions.auth.inc.php

@@ -4,22 +4,31 @@ function check_login($user, $pass, $app_passwd_data = false, $extra = null) {
   global $redis;
   
   $is_internal = $extra['is_internal'];
+  $role = $extra['role'];
 
   // Try validate admin
-  $result = admin_login($user, $pass);
-  if ($result !== false) return $result;
+  if (!isset($role) || $role == "admin") {
+    $result = admin_login($user, $pass);
+    if ($result !== false) return $result;
+  }
 
   // Try validate domain admin
-  $result = domainadmin_login($user, $pass);
-  if ($result !== false) return $result;
+  if (!isset($role) || $role == "domain_admin") {
+    $result = domainadmin_login($user, $pass);
+    if ($result !== false) return $result;
+  }
 
   // Try validate user
-  $result = user_login($user, $pass);
-  if ($result !== false) return $result;
+  if (!isset($role) || $role == "user") {
+    $result = user_login($user, $pass);
+    if ($result !== false) return $result;
+  }
 
   // Try validate app password
-  $result = apppass_login($user, $pass, $app_passwd_data);
-  if ($result !== false) return $result;
+  if (!isset($role) || $role == "app") {
+    $result = apppass_login($user, $pass, $app_passwd_data);
+    if ($result !== false) return $result;
+  }
 
   // skip log and only return false if it's an internal request
   if ($is_internal == true) return false;
@@ -175,62 +184,136 @@ function user_login($user, $pass, $extra = null){
   $stmt->execute(array(':user' => $user));
   $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
-  // user does not exist, try call keycloak login and create user if possible via rest flow
+  // user does not exist, try call idp login and create user if possible via rest flow
   if (!$row){
     $iam_settings = identity_provider('get');
     if ($iam_settings['authsource'] == 'keycloak' && intval($iam_settings['mailpassword_flow']) == 1){
       $result = keycloak_mbox_login_rest($user, $pass, $iam_settings, array('is_internal' => $is_internal, 'create' => true));
       if ($result !== false) return $result;
+    } else if ($iam_settings['authsource'] == 'ldap') {
+      $result = ldap_mbox_login($user, $pass, $iam_settings, array('is_internal' => $is_internal, 'create' => true));
+      if ($result !== false) return $result;
     }
   } 
   if ($row['active'] != 1) {
     return false;
   }
 
-  if ($row['authsource'] == 'keycloak'){
-    // user authsource is keycloak, try using via rest flow
-    $iam_settings = identity_provider('get');
-    if (intval($iam_settings['mailpassword_flow']) == 1){
-      $result = keycloak_mbox_login_rest($user, $pass, $iam_settings, array('is_internal' => $is_internal));
+  switch ($row['authsource']) {
+    case 'keycloak':
+      // user authsource is keycloak, try using via rest flow
+      $iam_settings = identity_provider('get');
+      if (intval($iam_settings['mailpassword_flow']) == 1){
+        $result = keycloak_mbox_login_rest($user, $pass, $iam_settings, array('is_internal' => $is_internal));
+        if ($result !== false) {
+          // check for tfa authenticators
+          $authenticators = get_tfa($user);
+          if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
+            // authenticators found, init TFA flow
+            $_SESSION['pending_mailcow_cc_username'] = $user;
+            $_SESSION['pending_mailcow_cc_role'] = "user";
+            $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
+            unset($_SESSION['ldelay']);
+            $_SESSION['return'][] =  array(
+              'type' => 'success',
+              'log' => array(__FUNCTION__, $user, '*'),
+              'msg' => array('logged_in_as', $user)
+            );
+            return "pending";
+          } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
+            // no authenticators found, login successfull
+            if (!$is_internal){
+              unset($_SESSION['ldelay']);
+              // Reactivate TFA if it was set to "deactivate TFA for next login"
+              $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
+              $stmt->execute(array(':user' => $user));
+              $_SESSION['return'][] =  array(
+                'type' => 'success',
+                'log' => array(__FUNCTION__, $user, '*'),
+                'msg' => array('logged_in_as', $user)
+              );
+            }
+            return "user";
+          }
+        }
+        return $result;
+      } else {
+        return false;
+      }
+    break;
+    case 'ldap':
+      // user authsource is ldap
+      $iam_settings = identity_provider('get');
+      $result = ldap_mbox_login($user, $pass, $iam_settings, array('is_internal' => $is_internal));
+      if ($result !== false) {
+        // check for tfa authenticators
+        $authenticators = get_tfa($user);
+        if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
+          // authenticators found, init TFA flow
+          $_SESSION['pending_mailcow_cc_username'] = $user;
+          $_SESSION['pending_mailcow_cc_role'] = "user";
+          $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
+          unset($_SESSION['ldelay']);
+          $_SESSION['return'][] =  array(
+            'type' => 'success',
+            'log' => array(__FUNCTION__, $user, '*'),
+            'msg' => array('logged_in_as', $user)
+          );
+          return "pending";
+        } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
+          // no authenticators found, login successfull
+          if (!$is_internal){
+            unset($_SESSION['ldelay']);
+            // Reactivate TFA if it was set to "deactivate TFA for next login"
+            $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
+            $stmt->execute(array(':user' => $user));
+            $_SESSION['return'][] =  array(
+              'type' => 'success',
+              'log' => array(__FUNCTION__, $user, '*'),
+              'msg' => array('logged_in_as', $user)
+            );
+          }
+          return "user";
+        }
+      }
       return $result;
-    } else {
-      return false;
-    }
-  }
- 
-  // verify password
-  if (verify_hash($row['password'], $pass) !== false) {
-    // check for tfa authenticators
-    $authenticators = get_tfa($user);
-    if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
-      // authenticators found, init TFA flow
-      $_SESSION['pending_mailcow_cc_username'] = $user;
-      $_SESSION['pending_mailcow_cc_role'] = "user";
-      $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
-      unset($_SESSION['ldelay']);
-      $_SESSION['return'][] =  array(
-        'type' => 'success',
-        'log' => array(__FUNCTION__, $user, '*'),
-        'msg' => array('logged_in_as', $user)
-      );
-      return "pending";
-    } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
-      // no authenticators found, login successfull
-      if (!$is_internal){
-        unset($_SESSION['ldelay']);
-        // Reactivate TFA if it was set to "deactivate TFA for next login"
-        $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
-        $stmt->execute(array(':user' => $user));
-        $_SESSION['return'][] =  array(
-          'type' => 'success',
-          'log' => array(__FUNCTION__, $user, '*'),
-          'msg' => array('logged_in_as', $user)
-        );
+    break;
+    default:
+      // verify password
+      if (verify_hash($row['password'], $pass) !== false) {
+        // check for tfa authenticators
+        $authenticators = get_tfa($user);
+        if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
+          // authenticators found, init TFA flow
+          $_SESSION['pending_mailcow_cc_username'] = $user;
+          $_SESSION['pending_mailcow_cc_role'] = "user";
+          $_SESSION['pending_tfa_methods'] = $authenticators['additional'];
+          unset($_SESSION['ldelay']);
+          $_SESSION['return'][] =  array(
+            'type' => 'success',
+            'log' => array(__FUNCTION__, $user, '*'),
+            'msg' => array('logged_in_as', $user)
+          );
+          return "pending";
+        } else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
+          // no authenticators found, login successfull
+          if (!$is_internal){
+            unset($_SESSION['ldelay']);
+            // Reactivate TFA if it was set to "deactivate TFA for next login"
+            $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
+            $stmt->execute(array(':user' => $user));
+            $_SESSION['return'][] =  array(
+              'type' => 'success',
+              'log' => array(__FUNCTION__, $user, '*'),
+              'msg' => array('logged_in_as', $user)
+            );
+          }
+          return "user";
+        }
       }
-      return "user";
-    }
+    break;
   }
-
+ 
   return false;
 }
 function apppass_login($user, $pass, $app_passwd_data, $extra = null){
@@ -372,11 +455,6 @@ function keycloak_mbox_login_rest($user, $pass, $iam_settings, $extra = null){
     return false;
   } else if (!$create) {
     // login success - dont create mailbox
-    $_SESSION['return'][] =  array(
-      'type' => 'success',
-      'log' => array(__FUNCTION__, $user, '*'),
-      'msg' => array('logged_in_as', $user)
-    );
     return 'user';
   }
 
@@ -388,16 +466,71 @@ function keycloak_mbox_login_rest($user, $pass, $iam_settings, $extra = null){
   $create_res = mailbox('add', 'mailbox_from_template', array(
     'domain' => explode('@', $user)[1],
     'local_part' => explode('@', $user)[0],
+    'name' => $user_res['firstName'] . " " . $user_res['lastName'],
     'authsource' => 'keycloak',
-    'template' => $iam_settings['mappers'][$mapper_key]
+    'template' => $iam_settings['templates'][$mapper_key]
   ));
   if (!$create_res) return false;
 
+  return 'user';
+}
+function ldap_mbox_login($user, $pass, $iam_settings, $extra = null){
+  global $pdo;
+  global $iam_provider;
+
+  $is_internal = $extra['is_internal'];
+  $create = $extra['create'];
+
+  if (!filter_var($user, FILTER_VALIDATE_EMAIL) && !ctype_alnum(str_replace(array('_', '.', '-'), '', $user))) {
+    if (!$is_internal){
+      $_SESSION['return'][] =  array(
+        'type' => 'danger',
+        'log' => array(__FUNCTION__, $user, '*'),
+        'msg' => 'malformed_username'
+      );
+    }
+    return false;
+  }
+
+  try {
+    $ldap_query = $iam_provider->query()
+      ->where($iam_settings['username_field'], '=', $user)
+      ->select([$iam_settings['username_field'], $iam_settings['attribute_field'], 'displayname', 'distinguishedname']);
+    if (!empty($iam_settings['filter'])) {
+      $ldap_query = $ldap_query->whereRaw($iam_settings['filter']);
+    }
+
+    $user_res = $ldap_query->firstOrFail();
+  } catch (Exception $e) {
+    return false;
+  }
+  if (!$iam_provider->auth()->attempt($user_res['distinguishedname'][0], $pass)) {
+    return false;
+  }
+
+  // get mapped template, if not set return false
+  // also return false if no mappers were defined
+  $user_template = $user_res[$iam_settings['attribute_field']][0];
+  if ($create && (empty($iam_settings['mappers']) || !$user_template)){
+    return false;
+  } else if (!$create) {
+    // login success - dont create mailbox
+    return 'user';
+  }
+
+  // check if matching attribute exist
+  $mapper_key = array_search($user_template, $iam_settings['mappers']);
+  if ($mapper_key === false) return false;
+
+  // create mailbox
+  $create_res = mailbox('add', 'mailbox_from_template', array(
+    'domain' => explode('@', $user)[1],
+    'local_part' => explode('@', $user)[0],
+    'name' => $user_res['displayname'][0],
+    'authsource' => 'ldap',
+    'template' => $iam_settings['templates'][$mapper_key]
+  ));
+  if (!$create_res) return false;
 
-  $_SESSION['return'][] =  array(
-    'type' => 'success',
-    'log' => array(__FUNCTION__, $user, '*'),
-    'msg' => array('logged_in_as', $user)
-  );
   return 'user';
 }

+ 186 - 89
data/web/inc/functions.inc.php

@@ -840,6 +840,11 @@ function update_sogo_static_view($mailbox = null) {
     }
   }
 
+  // generate random password for sogo to deny direct login
+  $random_password = base64_encode(openssl_random_pseudo_bytes(24));
+  $random_salt = base64_encode(openssl_random_pseudo_bytes(16));
+  $random_hash = '{SSHA256}' . base64_encode(hash('sha256', base64_decode($password) . $salt, true) . $salt);
+  
   $subquery = "GROUP BY mailbox.username";
   if ($mailbox_exists) {
     $subquery = "AND mailbox.username = :mailbox";
@@ -849,13 +854,7 @@ function update_sogo_static_view($mailbox = null) {
         mailbox.username,
         mailbox.domain,
         mailbox.username,
-        CASE 
-          WHEN mailbox.authsource IS NOT NULL AND mailbox.authsource <> 'mailcow' THEN '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'
-          ELSE 
-            IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0',
-              IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),
-              '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321')
-        END AS c_password,
+        :random_hash,
         mailbox.name,
         mailbox.username,
         IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''),
@@ -886,9 +885,15 @@ function update_sogo_static_view($mailbox = null) {
   
   if ($mailbox_exists) {
     $stmt = $pdo->prepare($query);
-    $stmt->execute(array(':mailbox' => $mailbox));
+    $stmt->execute(array(
+      ':random_hash' => $random_hash,
+      ':mailbox' => $mailbox
+    ));
   } else {
-    $stmt = $pdo->query($query);
+    $stmt = $pdo->prepare($query);
+    $stmt->execute(array(
+      ':random_hash' => $random_hash
+    ));
   }
   
   $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
@@ -1060,13 +1065,19 @@ function set_tfa($_data) {
 
   // check mailbox confirm password
   if ($access_denied === null) {
-    $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+    $stmt = $pdo->prepare("SELECT `password`, `authsource` FROM `mailbox`
         WHERE `username` = :username");
     $stmt->execute(array(':username' => $username));
     $row = $stmt->fetch(PDO::FETCH_ASSOC);
     if ($row) {
-      if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
-      else $access_denied = false;
+      if ($row['authsource'] == 'ldap'){
+        $iam_settings = identity_provider('get');
+        if (!ldap_mbox_login($username, $_data["confirm_password"], $iam_settings)) $access_denied = true;
+        else $access_denied = false;
+      } else {
+        if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true;
+        else $access_denied = false;
+      }
     }
   }
 
@@ -2129,12 +2140,18 @@ function identity_provider($_action, $_data = null, $_extra = null) {
         $_SESSION['return'][] =  array(
           'type' => 'danger',
           'log' => array(__FUNCTION__, $_action, $data_log),
-          'msg' => array('required_data_missing', $setting)
+          'msg' => array('required_data_missing', '')
         );
         return false;
       }
+
+      $available_authsources = array(
+        "keycloak",
+        "generic-oidc",
+        "ldap"
+      );
       $_data['authsource'] = strtolower($_data['authsource']);
-      if ($_data['authsource'] != "keycloak" && $_data['authsource'] != "generic-oidc"){
+      if (!in_array($_data['authsource'], $available_authsources)){
         $_SESSION['return'][] =  array(
           'type' => 'danger',
           'log' => array(__FUNCTION__, $_action, $data_log),
@@ -2158,20 +2175,33 @@ function identity_provider($_action, $_data = null, $_extra = null) {
         return false;
       }
 
-      if ($_data['authsource'] == "keycloak") {
-        $_data['server_url']        = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
-        $_data['mailpassword_flow'] = isset($_data['mailpassword_flow']) ? intval($_data['mailpassword_flow']) : 0;
-        $_data['periodic_sync']     = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
-        $_data['import_users']      = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
-        $_data['sync_interval']     = isset($_data['sync_interval']) ? intval($_data['sync_interval']) : 15;
-        $_data['sync_interval']     = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
-        $required_settings          = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval');
-      } else if ($_data['authsource'] == "generic-oidc") {
-        $_data['authorize_url']     = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
-        $_data['token_url']         = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
-        $_data['userinfo_url']      = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
-        $_data['client_scopes']     = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email";
-        $required_settings          = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes');
+      switch ($_data['authsource']) {
+        case "keycloak":
+          $_data['server_url']        = (!empty($_data['server_url'])) ? rtrim($_data['server_url'], '/') : null;
+          $_data['mailpassword_flow'] = isset($_data['mailpassword_flow']) ? intval($_data['mailpassword_flow']) : 0;
+          $_data['periodic_sync']     = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
+          $_data['import_users']      = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
+          $_data['sync_interval']     = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
+          $_data['sync_interval']     = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
+          $required_settings          = array('authsource', 'server_url', 'realm', 'client_id', 'client_secret', 'redirect_url', 'version', 'mailpassword_flow', 'periodic_sync', 'import_users', 'sync_interval');
+        break;
+        case "generic-oidc":
+          $_data['authorize_url']     = (!empty($_data['authorize_url'])) ? $_data['authorize_url'] : null;
+          $_data['token_url']         = (!empty($_data['token_url'])) ? $_data['token_url'] : null;
+          $_data['userinfo_url']      = (!empty($_data['userinfo_url'])) ? $_data['userinfo_url'] : null;
+          $_data['client_scopes']     = (!empty($_data['client_scopes'])) ? $_data['client_scopes'] : "openid profile email";
+          $required_settings          = array('authsource', 'authorize_url', 'token_url', 'client_id', 'client_secret', 'redirect_url', 'userinfo_url', 'client_scopes');
+        break;
+        case "ldap":
+          $_data['port']              = (!empty($_data['port'])) ? intval($_data['port']) : 389;
+          $_data['username_field']    = (!empty($_data['username_field'])) ? $_data['username_field'] : "mail";
+          $_data['filter']            = (!empty($_data['filter'])) ? $_data['filter'] : "";
+          $_data['periodic_sync']     = isset($_data['periodic_sync']) ? intval($_data['periodic_sync']) : 0;
+          $_data['import_users']      = isset($_data['import_users']) ? intval($_data['import_users']) : 0;
+          $_data['sync_interval']     = (!empty($_data['sync_interval'])) ? intval($_data['sync_interval']) : 15;
+          $_data['sync_interval']     = $_data['sync_interval'] < 1 ? 1 : $_data['sync_interval'];
+          $required_settings          = array('authsource', 'host', 'port', 'basedn', 'username_field', 'filter', 'attribute_field', 'binddn', 'bindpass', 'periodic_sync', 'import_users', 'sync_interval');
+        break;
       }
       
       $pdo->beginTransaction();
@@ -2234,30 +2264,56 @@ function identity_provider($_action, $_data = null, $_extra = null) {
         return false;
       }
 
-      if ($_data['authsource'] == 'keycloak') {
-        $url = "{$_data['server_url']}/realms/{$_data['realm']}/protocol/openid-connect/token";
-      } else {
-        $url = $_data['token_url'];
-      }
-      $req = http_build_query(array(
-        'grant_type'    => 'client_credentials',
-        'client_id'     => $_data['client_id'],
-        'client_secret' => $_data['client_secret']
-      ));
-      $curl = curl_init();
-      curl_setopt($curl, CURLOPT_URL, $url);
-      curl_setopt($curl, CURLOPT_TIMEOUT, 7);
-      curl_setopt($curl, CURLOPT_POST, 1);
-      curl_setopt($curl, CURLOPT_POSTFIELDS, $req);
-      curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
-      curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
-      $res = curl_exec($curl);
-      $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-      curl_close ($curl);
-      
-      if ($code != 200) {
-        return false;
+      switch ($_data['authsource']) {
+        case 'keycloak':
+        case 'generic-oidc':
+          if ($_data['authsource'] == 'keycloak') {
+            $url = "{$_data['server_url']}/realms/{$_data['realm']}/protocol/openid-connect/token";
+          } else {
+            $url = $_data['token_url'];
+          }
+          $req = http_build_query(array(
+            'grant_type'    => 'client_credentials',
+            'client_id'     => $_data['client_id'],
+            'client_secret' => $_data['client_secret']
+          ));
+          $curl = curl_init();
+          curl_setopt($curl, CURLOPT_URL, $url);
+          curl_setopt($curl, CURLOPT_TIMEOUT, 7);
+          curl_setopt($curl, CURLOPT_POST, 1);
+          curl_setopt($curl, CURLOPT_POSTFIELDS, $req);
+          curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
+          curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+          $res = curl_exec($curl);
+          $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+          curl_close ($curl);
+          
+          if ($code != 200) {
+            return false;
+          }
+        break;
+        case 'ldap':
+          if (!$_data['host'] || !$_data['port'] || !$_data['basedn'] ||
+            !$_data['binddn'] || !$_data['bindpass']){
+              return false;
+          }
+          $provider = new \LdapRecord\Connection([
+            'hosts'                     => [$_data['host']],
+            'port'                      => $_data['port'],
+            'base_dn'                   => $_data['basedn'],
+            'username'                  => $_data['binddn'],
+            'password'                  => $_data['bindpass']
+          ]);
+          try {
+            $provider->connect();
+          } catch (TypeError $e) {
+            return false;
+          } catch (\LdapRecord\Auth\BindException $e) {
+            return false;
+          }
+        break;
       }
+
       return true;
     break;
     case "delete":
@@ -2295,40 +2351,70 @@ function identity_provider($_action, $_data = null, $_extra = null) {
     case "init":
       $iam_settings = identity_provider('get');
       $provider = null;
-      if ($iam_settings['authsource'] == 'keycloak'){
-        if ($iam_settings['server_url'] && $iam_settings['realm'] && $iam_settings['client_id'] &&
+
+      switch ($iam_settings['authsource']) {
+        case "keycloak":
+          if ($iam_settings['server_url'] && $iam_settings['realm'] && $iam_settings['client_id'] &&
             $iam_settings['client_secret'] && $iam_settings['redirect_url'] && $iam_settings['version']){
-          $provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([
-            'authServerUrl'         => $iam_settings['server_url'],
-            'realm'                 => $iam_settings['realm'],
-            'clientId'              => $iam_settings['client_id'],
-            'clientSecret'          => $iam_settings['client_secret'],
-            'redirectUri'           => $iam_settings['redirect_url'],
-            'version'               => $iam_settings['version'],                            
-            // 'encryptionAlgorithm'   => 'RS256',                             // optional
-            // 'encryptionKeyPath'     => '../key.pem'                         // optional
-            // 'encryptionKey'         => 'contents_of_key_or_certificate'     // optional
-          ]);
-        }
-      }
-      else if ($iam_settings['authsource'] == 'generic-oidc'){
-        if ($iam_settings['client_id'] && $iam_settings['client_secret'] && $iam_settings['redirect_url'] &&
+            $provider = new Stevenmaguire\OAuth2\Client\Provider\Keycloak([
+              'authServerUrl'         => $iam_settings['server_url'],
+              'realm'                 => $iam_settings['realm'],
+              'clientId'              => $iam_settings['client_id'],
+              'clientSecret'          => $iam_settings['client_secret'],
+              'redirectUri'           => $iam_settings['redirect_url'],
+              'version'               => $iam_settings['version'],                            
+              // 'encryptionAlgorithm'   => 'RS256',                             // optional
+              // 'encryptionKeyPath'     => '../key.pem'                         // optional
+              // 'encryptionKey'         => 'contents_of_key_or_certificate'     // optional
+            ]);
+          }
+        break;
+        case "generic-oidc":
+          if ($iam_settings['client_id'] && $iam_settings['client_secret'] && $iam_settings['redirect_url'] &&
             $iam_settings['authorize_url'] && $iam_settings['token_url'] && $iam_settings['userinfo_url']){
-          $provider = new \League\OAuth2\Client\Provider\GenericProvider([
-            'clientId'                => $iam_settings['client_id'],
-            'clientSecret'            => $iam_settings['client_secret'],
-            'redirectUri'             => $iam_settings['redirect_url'],
-            'urlAuthorize'            => $iam_settings['authorize_url'],
-            'urlAccessToken'          => $iam_settings['token_url'],
-            'urlResourceOwnerDetails' => $iam_settings['userinfo_url'],
-            'scopes'                  => $iam_settings['client_scopes']
-          ]);
-        }
+            $provider = new \League\OAuth2\Client\Provider\GenericProvider([
+              'clientId'                => $iam_settings['client_id'],
+              'clientSecret'            => $iam_settings['client_secret'],
+              'redirectUri'             => $iam_settings['redirect_url'],
+              'urlAuthorize'            => $iam_settings['authorize_url'],
+              'urlAccessToken'          => $iam_settings['token_url'],
+              'urlResourceOwnerDetails' => $iam_settings['userinfo_url'],
+              'scopes'                  => $iam_settings['client_scopes']
+            ]);
+          }
+        break;
+        case "ldap":
+          if ($iam_settings['host'] && $iam_settings['port'] && $iam_settings['basedn'] &&
+            $iam_settings['binddn'] && $iam_settings['bindpass']){
+            $provider = new \LdapRecord\Connection([
+              'hosts'                     => [$iam_settings['host']],
+              'port'                      => $iam_settings['port'],
+              'base_dn'                   => $iam_settings['basedn'],
+              'username'                  => $iam_settings['binddn'],
+              'password'                  => $iam_settings['bindpass']
+            ]);
+            try {
+              $provider->connect();
+            } catch (TypeError $e) {
+              $provider = null;
+            }
+          }
+        break;
       }
+
       return $provider;
     break;
     case "verify-sso":
       $provider = $_data['iam_provider'];
+      $iam_settings = identity_provider('get');
+      if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc'){
+        $_SESSION['return'][] =  array(
+          'type' => 'danger',
+          'log' => array(__FUNCTION__),
+          'msg' => array('login_failed', "no OIDC provider configured")
+        );
+        return false;
+      }
     
       try {
         $token = $provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
@@ -2358,8 +2444,7 @@ function identity_provider($_action, $_data = null, $_extra = null) {
       $row = $stmt->fetch(PDO::FETCH_ASSOC);
       if ($row){
         // success
-        $_SESSION['mailcow_cc_username'] = $info['email'];
-        $_SESSION['mailcow_cc_role'] = "user";
+        set_user_loggedin_session($info['email']);
         $_SESSION['return'][] =  array(
           'type' => 'success',
           'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
@@ -2370,9 +2455,8 @@ function identity_provider($_action, $_data = null, $_extra = null) {
 
       // get mapped template, if not set return false
       // also return false if no mappers were defined
-      $provider = identity_provider('get');
       $user_template = $info['mailcow_template'];
-      if (empty($provider['mappers']) || empty($user_template)){
+      if (empty($iam_settings['mappers']) || empty($user_template)){
         clear_session();  
         $_SESSION['return'][] =  array(
           'type' => 'danger',
@@ -2383,7 +2467,7 @@ function identity_provider($_action, $_data = null, $_extra = null) {
       }
 
       // check if matching attribute exist
-      $mapper_key = array_search($user_template, $provider['mappers']);
+      $mapper_key = array_search($user_template, $iam_settings['mappers']);
       if ($mapper_key === false) {
         clear_session();  
         $_SESSION['return'][] =  array(
@@ -2398,8 +2482,9 @@ function identity_provider($_action, $_data = null, $_extra = null) {
       $create_res = mailbox('add', 'mailbox_from_template', array(
         'domain' => explode('@', $info['email'])[1],
         'local_part' => explode('@', $info['email'])[0],
-        'authsource' => identity_provider('get')['authsource'],
-        'template' => $provider['templates'][$mapper_key]
+        'name' => $info['firstName'] . " " . $info['lastName'],
+        'authsource' => $iam_settings['authsource'],
+        'template' => $iam_settings['templates'][$mapper_key]
       ));
       if (!$create_res){
         clear_session();  
@@ -2411,8 +2496,7 @@ function identity_provider($_action, $_data = null, $_extra = null) {
         return false;
       }
     
-      $_SESSION['mailcow_cc_username'] = $info['email'];
-      $_SESSION['mailcow_cc_role'] = "user";
+      set_user_loggedin_session($info['email']);
       $_SESSION['return'][] =  array(
         'type' => 'success',
         'log' => array(__FUNCTION__, $_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role']),
@@ -2429,10 +2513,11 @@ function identity_provider($_action, $_data = null, $_extra = null) {
         $_SESSION['iam_refresh_token'] = $token->getRefreshToken();
         $info = $provider->getResourceOwner($token)->toArray();
       } catch (Throwable $e) {
+        clear_session();  
         $_SESSION['return'][] =  array(
           'type' => 'danger',
           'log' => array(__FUNCTION__),
-          'msg' => array('login_failed', $e->getMessage())
+          'msg' => array('refresh_login_failed', $e->getMessage())
         );
         return false;
       }
@@ -2452,6 +2537,9 @@ function identity_provider($_action, $_data = null, $_extra = null) {
       return true;
     break;
     case "get-redirect":
+      $iam_settings = identity_provider('get');
+      if ($iam_settings['authsource'] != 'keycloak' && $iam_settings['authsource'] != 'generic-oidc') 
+        return false;
       $provider = $_data['iam_provider'];
       $authUrl = $provider->getAuthorizationUrl();
       $_SESSION['oauth2state'] = $provider->getState();
@@ -2522,7 +2610,16 @@ function clear_session(){
   session_destroy();
   session_write_close();
 }
-
+function set_user_loggedin_session($user) {
+  $_SESSION['mailcow_cc_username'] = $user;
+  $_SESSION['mailcow_cc_role'] = 'user';
+  $sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
+  $_SESSION['sogo-sso-user-allowed'][] = $user;
+  $_SESSION['sogo-sso-pass'] = $sogo_sso_pass;
+  unset($_SESSION['pending_mailcow_cc_username']);
+  unset($_SESSION['pending_mailcow_cc_role']);
+  unset($_SESSION['pending_tfa_methods']);
+}
 function get_logs($application, $lines = false) {
   if ($lines === false) {
     $lines = $GLOBALS['LOG_LINES'] - 1;

+ 5 - 3
data/web/inc/functions.mailbox.inc.php

@@ -1019,7 +1019,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             );
             return false;
           }
-          if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc'))){
+          if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc', 'ldap'))){
             $authsource = $_data['authsource'];
           }
           if (empty($name)) {
@@ -2944,7 +2944,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               $tags           = (is_array($_data['tags']) ? $_data['tags'] : array());
               $attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : '';
               $authsource     = $is_now['authsource'];
-              if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc'))){
+              if (in_array($_data['authsource'], array('mailcow', 'keycloak', 'generic-oidc', 'ldap'))){
                 $authsource = $_data['authsource'];
               }
             }
@@ -3285,11 +3285,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
 
           $attribute_hash = sha1(json_encode($mbox_template_data["attributes"]));
           $is_now = mailbox('get', 'mailbox_details', $_data['username']);
-          if ($is_now['attributes']['attribute_hash'] == $attribute_hash)
+          $name = ltrim(rtrim($_data['name'], '>'), '<');
+          if ($is_now['attributes']['attribute_hash'] == $attribute_hash && $is_now['name'] == $name)
             return true;
 
           $mbox_template_data = json_decode($mbox_template_data["attributes"], true);
           $mbox_template_data['attribute_hash'] = $attribute_hash;
+          $mbox_template_data['name'] = $name;
           $quarantine_attributes = array('username' => $_data['username']);
           $tls_attributes = array('username' => $_data['username']);
           $ratelimit_attributes = array('object' => $_data['username']);

+ 1 - 1
data/web/inc/init_db.inc.php

@@ -362,7 +362,7 @@ function init_db_schema() {
           "custom_attributes" => "JSON NOT NULL DEFAULT ('{}')",
           "kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
           "multiple_bookings" => "INT NOT NULL DEFAULT -1",
-          "authsource" => "ENUM('mailcow', 'keycloak', 'generic-oidc') DEFAULT 'mailcow'",
+          "authsource" => "ENUM('mailcow', 'keycloak', 'generic-oidc', 'ldap') DEFAULT 'mailcow'",
           "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
           "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
           "active" => "TINYINT(1) NOT NULL DEFAULT '1'"

+ 1 - 1
data/web/inc/lib/composer.json

@@ -8,7 +8,7 @@
         "matthiasmullie/minify": "^1.3",
         "bshaffer/oauth2-server-php": "^1.11",
         "mustangostang/spyc": "^0.6.3",
-        "directorytree/ldaprecord": "^2.4",
+        "directorytree/ldaprecord": "^3.3",
         "twig/twig": "^3.0",
         "stevenmaguire/oauth2-keycloak": "^4.0",
         "league/oauth2-client": "^2.7"

+ 230 - 110
data/web/inc/lib/composer.lock

@@ -68,6 +68,75 @@
             },
             "time": "2018-12-04T00:29:32+00:00"
         },
+        {
+            "name": "carbonphp/carbon-doctrine-types",
+            "version": "3.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "conflict": {
+                "doctrine/dbal": "<4.0.0 || >=5.0.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^4.0.0",
+                "nesbot/carbon": "^2.71.0 || ^3.0.0",
+                "phpunit/phpunit": "^10.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "KyleKatarn",
+                    "email": "kylekatarnls@gmail.com"
+                }
+            ],
+            "description": "Types to use Carbon in Doctrine",
+            "keywords": [
+                "carbon",
+                "date",
+                "datetime",
+                "doctrine",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+                "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2024-02-09T16:56:22+00:00"
+        },
         {
             "name": "ddeboer/imap",
             "version": "1.13.1",
@@ -145,27 +214,28 @@
         },
         {
             "name": "directorytree/ldaprecord",
-            "version": "v2.10.1",
+            "version": "v2.20.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/DirectoryTree/LdapRecord.git",
-                "reference": "bf512d9af7a7b0e2ed7a666ab29cefdd027bee88"
+                "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/bf512d9af7a7b0e2ed7a666ab29cefdd027bee88",
-                "reference": "bf512d9af7a7b0e2ed7a666ab29cefdd027bee88",
+                "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/5bd0a5a9d257cf1049ae83055dbba4c3479ddf16",
+                "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "ext-ldap": "*",
-                "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
+                "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0",
                 "nesbot/carbon": "^1.0|^2.0",
                 "php": ">=7.3",
-                "psr/log": "*",
+                "psr/log": "^1.0|^2.0|^3.0",
                 "psr/simple-cache": "^1.0|^2.0",
-                "tightenco/collect": "^5.6|^6.0|^7.0|^8.0"
+                "symfony/polyfill-php80": "^1.25",
+                "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0"
             },
             "require-dev": {
                 "mockery/mockery": "^1.0",
@@ -214,7 +284,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2022-02-25T16:00:51+00:00"
+            "time": "2023-10-11T16:34:34+00:00"
         },
         {
             "name": "firebase/php-jwt",
@@ -609,27 +679,27 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v9.3.0",
+            "version": "v10.44.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
-                "reference": "bf4b3c254c49d28157645d01e4883b5951b1e1d0"
+                "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/contracts/zipball/bf4b3c254c49d28157645d01e4883b5951b1e1d0",
-                "reference": "bf4b3c254c49d28157645d01e4883b5951b1e1d0",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
+                "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
                 "shasum": ""
             },
             "require": {
-                "php": "^8.0.2",
+                "php": "^8.1",
                 "psr/container": "^1.1.1|^2.0.1",
                 "psr/simple-cache": "^1.0|^2.0|^3.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.x-dev"
+                    "dev-master": "10.x-dev"
                 }
             },
             "autoload": {
@@ -653,7 +723,7 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2022-02-22T14:45:39+00:00"
+            "time": "2024-01-15T18:52:32+00:00"
         },
         {
             "name": "league/oauth2-client",
@@ -908,34 +978,41 @@
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.57.0",
+            "version": "2.72.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "4a54375c21eea4811dbd1149fe6b246517554e78"
+                "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4a54375c21eea4811dbd1149fe6b246517554e78",
-                "reference": "4a54375c21eea4811dbd1149fe6b246517554e78",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/0c6fd108360c562f6e4fd1dedb8233b423e91c83",
+                "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83",
                 "shasum": ""
             },
             "require": {
+                "carbonphp/carbon-doctrine-types": "*",
                 "ext-json": "*",
                 "php": "^7.1.8 || ^8.0",
+                "psr/clock": "^1.0",
                 "symfony/polyfill-mbstring": "^1.0",
                 "symfony/polyfill-php80": "^1.16",
                 "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
             },
+            "provide": {
+                "psr/clock-implementation": "1.0"
+            },
             "require-dev": {
-                "doctrine/dbal": "^2.0 || ^3.0",
-                "doctrine/orm": "^2.7",
+                "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0",
+                "doctrine/orm": "^2.7 || ^3.0",
                 "friendsofphp/php-cs-fixer": "^3.0",
                 "kylekatarnls/multi-tester": "^2.0",
+                "ondrejmirtes/better-reflection": "*",
                 "phpmd/phpmd": "^2.9",
                 "phpstan/extension-installer": "^1.0",
-                "phpstan/phpstan": "^0.12.54 || ^1.0",
-                "phpunit/phpunit": "^7.5.20 || ^8.5.14",
+                "phpstan/phpstan": "^0.12.99 || ^1.7.14",
+                "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
                 "squizlabs/php_codesniffer": "^3.4"
             },
             "bin": [
@@ -992,15 +1069,19 @@
             },
             "funding": [
                 {
-                    "url": "https://opencollective.com/Carbon",
-                    "type": "open_collective"
+                    "url": "https://github.com/sponsors/kylekatarnls",
+                    "type": "github"
                 },
                 {
-                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "url": "https://opencollective.com/Carbon#sponsor",
+                    "type": "opencollective"
+                },
+                {
+                    "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-02-13T18:13:33+00:00"
+            "time": "2024-01-25T10:35:09+00:00"
         },
         {
             "name": "paragonie/random_compat",
@@ -1221,6 +1302,54 @@
             ],
             "time": "2022-02-28T15:31:21+00:00"
         },
+        {
+            "name": "psr/clock",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/clock.git",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Clock\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for reading the clock.",
+            "homepage": "https://github.com/php-fig/clock",
+            "keywords": [
+                "clock",
+                "now",
+                "psr",
+                "psr-20",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/clock/issues",
+                "source": "https://github.com/php-fig/clock/tree/1.0.0"
+            },
+            "time": "2022-11-25T14:36:26+00:00"
+        },
         {
             "name": "psr/container",
             "version": "2.0.2",
@@ -1767,16 +1896,16 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v3.2.1",
+            "version": "v3.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
+                "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
-                "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
+                "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
                 "shasum": ""
             },
             "require": {
@@ -1785,7 +1914,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "3.3-dev"
+                    "dev-main": "3.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -1814,7 +1943,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
             },
             "funding": [
                 {
@@ -1830,7 +1959,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2023-03-01T10:25:55+00:00"
+            "time": "2023-05-23T14:45:45+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
@@ -1916,16 +2045,16 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.24.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
-                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
                 "shasum": ""
             },
             "require": {
@@ -1939,9 +2068,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.23-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -1979,7 +2105,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -1995,20 +2121,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-11-30T18:21:41+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.24.0",
+            "version": "v1.29.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
-                "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
                 "shasum": ""
             },
             "require": {
@@ -2016,9 +2142,6 @@
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.23-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -2062,7 +2185,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0"
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -2078,32 +2201,35 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-09-13T13:58:33+00:00"
+            "time": "2024-01-29T20:11:03+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v6.0.5",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875"
+                "reference": "637c51191b6b184184bbf98937702bcf554f7d04"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/e69501c71107cc3146b32aaa45f4edd0c3427875",
-                "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/637c51191b6b184184bbf98937702bcf554f7d04",
+                "reference": "637c51191b6b184184bbf98937702bcf554f7d04",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2",
+                "php": ">=8.1",
+                "symfony/deprecation-contracts": "^2.5|^3",
                 "symfony/polyfill-mbstring": "~1.0",
-                "symfony/translation-contracts": "^2.3|^3.0"
+                "symfony/translation-contracts": "^2.5|^3.0"
             },
             "conflict": {
                 "symfony/config": "<5.4",
                 "symfony/console": "<5.4",
                 "symfony/dependency-injection": "<5.4",
+                "symfony/http-client-contracts": "<2.5",
                 "symfony/http-kernel": "<5.4",
+                "symfony/service-contracts": "<2.5",
                 "symfony/twig-bundle": "<5.4",
                 "symfony/yaml": "<5.4"
             },
@@ -2111,22 +2237,19 @@
                 "symfony/translation-implementation": "2.3|3.0"
             },
             "require-dev": {
+                "nikic/php-parser": "^4.18|^5.0",
                 "psr/log": "^1|^2|^3",
-                "symfony/config": "^5.4|^6.0",
-                "symfony/console": "^5.4|^6.0",
-                "symfony/dependency-injection": "^5.4|^6.0",
-                "symfony/finder": "^5.4|^6.0",
-                "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
-                "symfony/http-kernel": "^5.4|^6.0",
-                "symfony/intl": "^5.4|^6.0",
+                "symfony/config": "^5.4|^6.0|^7.0",
+                "symfony/console": "^5.4|^6.0|^7.0",
+                "symfony/dependency-injection": "^5.4|^6.0|^7.0",
+                "symfony/finder": "^5.4|^6.0|^7.0",
+                "symfony/http-client-contracts": "^2.5|^3.0",
+                "symfony/http-kernel": "^5.4|^6.0|^7.0",
+                "symfony/intl": "^5.4|^6.0|^7.0",
                 "symfony/polyfill-intl-icu": "^1.21",
-                "symfony/service-contracts": "^1.1.2|^2|^3",
-                "symfony/yaml": "^5.4|^6.0"
-            },
-            "suggest": {
-                "psr/log-implementation": "To use logging capability in translator",
-                "symfony/config": "",
-                "symfony/yaml": ""
+                "symfony/routing": "^5.4|^6.0|^7.0",
+                "symfony/service-contracts": "^2.5|^3",
+                "symfony/yaml": "^5.4|^6.0|^7.0"
             },
             "type": "library",
             "autoload": {
@@ -2157,7 +2280,7 @@
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v6.0.5"
+                "source": "https://github.com/symfony/translation/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2173,32 +2296,29 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-02-09T15:52:48+00:00"
+            "time": "2024-01-29T13:11:52+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v3.0.0",
+            "version": "v3.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77"
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1b6ea5a7442af5a12dba3dbd6d71034b5b234e77",
-                "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7",
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2"
-            },
-            "suggest": {
-                "symfony/translation-implementation": ""
+                "php": ">=8.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "3.0-dev"
+                    "dev-main": "3.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -2208,7 +2328,10 @@
             "autoload": {
                 "psr-4": {
                     "Symfony\\Contracts\\Translation\\": ""
-                }
+                },
+                "exclude-from-classmap": [
+                    "/Test/"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -2235,7 +2358,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v3.0.0"
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1"
             },
             "funding": [
                 {
@@ -2251,42 +2374,39 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-09-07T12:43:40+00:00"
+            "time": "2023-12-26T14:02:43+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v6.0.5",
+            "version": "v6.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce"
+                "reference": "0435a08f69125535336177c29d56af3abc1f69da"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/60d6a756d5f485df5e6e40b337334848f79f61ce",
-                "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0435a08f69125535336177c29d56af3abc1f69da",
+                "reference": "0435a08f69125535336177c29d56af3abc1f69da",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2",
+                "php": ">=8.1",
+                "symfony/deprecation-contracts": "^2.5|^3",
                 "symfony/polyfill-mbstring": "~1.0"
             },
             "conflict": {
-                "phpunit/phpunit": "<5.4.3",
                 "symfony/console": "<5.4"
             },
             "require-dev": {
                 "ext-iconv": "*",
-                "symfony/console": "^5.4|^6.0",
-                "symfony/process": "^5.4|^6.0",
-                "symfony/uid": "^5.4|^6.0",
+                "symfony/console": "^5.4|^6.0|^7.0",
+                "symfony/error-handler": "^6.3|^7.0",
+                "symfony/http-kernel": "^5.4|^6.0|^7.0",
+                "symfony/process": "^5.4|^6.0|^7.0",
+                "symfony/uid": "^5.4|^6.0|^7.0",
                 "twig/twig": "^2.13|^3.0.4"
             },
-            "suggest": {
-                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
-                "ext-intl": "To show region name in time zone dump",
-                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
-            },
             "bin": [
                 "Resources/bin/var-dump-server"
             ],
@@ -2323,7 +2443,7 @@
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v6.0.5"
+                "source": "https://github.com/symfony/var-dumper/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2339,24 +2459,24 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-02-21T17:15:17+00:00"
+            "time": "2024-01-23T14:53:30+00:00"
         },
         {
             "name": "tightenco/collect",
-            "version": "v8.83.2",
+            "version": "v9.52.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tighten/collect.git",
-                "reference": "d9c66d586ec2d216d8a31283d73f8df1400cc722"
+                "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tighten/collect/zipball/d9c66d586ec2d216d8a31283d73f8df1400cc722",
-                "reference": "d9c66d586ec2d216d8a31283d73f8df1400cc722",
+                "url": "https://api.github.com/repos/tighten/collect/zipball/b15143cd11fe01a700fcc449df61adc64452fa6d",
+                "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.3|^8.0",
+                "php": "^8.0",
                 "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0 || ^6.0"
             },
             "require-dev": {
@@ -2391,9 +2511,9 @@
             ],
             "support": {
                 "issues": "https://github.com/tighten/collect/issues",
-                "source": "https://github.com/tighten/collect/tree/v8.83.2"
+                "source": "https://github.com/tighten/collect/tree/v9.52.7"
             },
-            "time": "2022-02-16T16:15:54+00:00"
+            "time": "2023-04-14T21:51:36+00:00"
         },
         {
             "name": "twig/twig",
@@ -2480,5 +2600,5 @@
     "prefer-lowest": false,
     "platform": [],
     "platform-dev": [],
-    "plugin-api-version": "2.3.0"
+    "plugin-api-version": "2.6.0"
 }

+ 15 - 2
data/web/inc/lib/vendor/autoload.php

@@ -3,8 +3,21 @@
 // autoload.php @generated by Composer
 
 if (PHP_VERSION_ID < 50600) {
-    echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
-    exit(1);
+    if (!headers_sent()) {
+        header('HTTP/1.1 500 Internal Server Error');
+    }
+    $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+    if (!ini_get('display_errors')) {
+        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+            fwrite(STDERR, $err);
+        } elseif (!headers_sent()) {
+            echo $err;
+        }
+    }
+    trigger_error(
+        $err,
+        E_USER_ERROR
+    );
 }
 
 require_once __DIR__ . '/composer/autoload_real.php';

+ 34 - 12
data/web/inc/lib/vendor/bin/carbon

@@ -12,6 +12,7 @@
 
 namespace Composer;
 
+$GLOBALS['_composer_bin_dir'] = __DIR__;
 $GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
 
 if (PHP_VERSION_ID < 80000) {
@@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) {
         {
             private $handle;
             private $position;
+            private $realpath;
 
             public function stream_open($path, $mode, $options, &$opened_path)
             {
-                // get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution
-                $opened_path = substr($path, 21);
-                $opened_path = realpath($opened_path) ?: $opened_path;
-                $this->handle = fopen($opened_path, $mode);
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
                 $this->position = 0;
 
-                // remove all traces of this stream wrapper once it has been used
-                stream_wrapper_unregister('composer-bin-proxy');
-
                 return (bool) $this->handle;
             }
 
@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
                 return $operation ? flock($this->handle, $operation) : true;
             }
 
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
             public function stream_tell()
             {
                 return $this->position;
@@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) {
 
             public function stream_stat()
             {
-                return fstat($this->handle);
+                return array();
             }
 
             public function stream_set_option($option, $arg1, $arg2)
             {
                 return true;
             }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
         }
     }
 
-    if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) {
-        include("composer-bin-proxy://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
-        exit(0);
+    if (
+        (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
+        || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
+    ) {
+        return include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
     }
 }
 
-include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';
+return include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';

+ 34 - 12
data/web/inc/lib/vendor/bin/var-dump-server

@@ -12,6 +12,7 @@
 
 namespace Composer;
 
+$GLOBALS['_composer_bin_dir'] = __DIR__;
 $GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
 
 if (PHP_VERSION_ID < 80000) {
@@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) {
         {
             private $handle;
             private $position;
+            private $realpath;
 
             public function stream_open($path, $mode, $options, &$opened_path)
             {
-                // get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution
-                $opened_path = substr($path, 21);
-                $opened_path = realpath($opened_path) ?: $opened_path;
-                $this->handle = fopen($opened_path, $mode);
+                // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
+                $opened_path = substr($path, 17);
+                $this->realpath = realpath($opened_path) ?: $opened_path;
+                $opened_path = $this->realpath;
+                $this->handle = fopen($this->realpath, $mode);
                 $this->position = 0;
 
-                // remove all traces of this stream wrapper once it has been used
-                stream_wrapper_unregister('composer-bin-proxy');
-
                 return (bool) $this->handle;
             }
 
@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
                 return $operation ? flock($this->handle, $operation) : true;
             }
 
+            public function stream_seek($offset, $whence)
+            {
+                if (0 === fseek($this->handle, $offset, $whence)) {
+                    $this->position = ftell($this->handle);
+                    return true;
+                }
+
+                return false;
+            }
+
             public function stream_tell()
             {
                 return $this->position;
@@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) {
 
             public function stream_stat()
             {
-                return fstat($this->handle);
+                return array();
             }
 
             public function stream_set_option($option, $arg1, $arg2)
             {
                 return true;
             }
+
+            public function url_stat($path, $flags)
+            {
+                $path = substr($path, 17);
+                if (file_exists($path)) {
+                    return stat($path);
+                }
+
+                return false;
+            }
         }
     }
 
-    if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) {
-        include("composer-bin-proxy://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
-        exit(0);
+    if (
+        (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
+        || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
+    ) {
+        return include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
     }
 }
 
-include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';
+return include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';

+ 21 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Carbon
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 14 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/README.md

@@ -0,0 +1,14 @@
+# carbonphp/carbon-doctrine-types
+
+Types to use Carbon in Doctrine
+
+## Documentation
+
+[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/)
+
+This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
+from `nestbot/carbon` package.
+
+Externalization allows to better deal with different versions of dbal. With
+version 4.0 of dbal, it no longer sustainable to be compatible with all version
+using a single code.

+ 36 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/composer.json

@@ -0,0 +1,36 @@
+{
+    "name": "carbonphp/carbon-doctrine-types",
+    "description": "Types to use Carbon in Doctrine",
+    "type": "library",
+    "keywords": [
+        "date",
+        "time",
+        "DateTime",
+        "Carbon",
+        "Doctrine"
+    ],
+    "require": {
+        "php": "^8.1"
+    },
+    "require-dev": {
+        "doctrine/dbal": "^4.0.0",
+        "nesbot/carbon": "^2.71.0 || ^3.0.0",
+        "phpunit/phpunit": "^10.3"
+    },
+    "conflict": {
+        "doctrine/dbal": "<4.0.0 || >=5.0.0"
+    },
+    "license": "MIT",
+    "autoload": {
+        "psr-4": {
+            "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+        }
+    },
+    "authors": [
+        {
+            "name": "KyleKatarn",
+            "email": "kylekatarnls@gmail.com"
+        }
+    ],
+    "minimum-stability": "dev"
+}

+ 16 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php

@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+
+interface CarbonDoctrineType
+{
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform);
+
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform);
+
+    public function convertToDatabaseValue($value, AbstractPlatform $platform);
+}

+ 9 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
+{
+}

+ 9 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php

@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+class CarbonType extends DateTimeType implements CarbonDoctrineType
+{
+}

+ 50 - 42
data/web/inc/lib/vendor/nesbot/carbon/src/Carbon/Doctrine/CarbonTypeConverter.php → data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php

@@ -1,13 +1,6 @@
 <?php
 
-/**
- * This file is part of the Carbon package.
- *
- * (c) Brian Nesbitt <brian@nesbot.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+declare(strict_types=1);
 
 namespace Carbon\Doctrine;
 
@@ -15,7 +8,12 @@ use Carbon\Carbon;
 use Carbon\CarbonInterface;
 use DateTimeInterface;
 use Doctrine\DBAL\Platforms\AbstractPlatform;
-use Doctrine\DBAL\Types\ConversionException;
+use Doctrine\DBAL\Platforms\DB2Platform;
+use Doctrine\DBAL\Platforms\OraclePlatform;
+use Doctrine\DBAL\Platforms\SQLitePlatform;
+use Doctrine\DBAL\Platforms\SQLServerPlatform;
+use Doctrine\DBAL\Types\Exception\InvalidType;
+use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
 use Exception;
 
 /**
@@ -23,6 +21,14 @@ use Exception;
  */
 trait CarbonTypeConverter
 {
+    /**
+     * This property differentiates types installed by carbonphp/carbon-doctrine-types
+     * from the ones embedded previously in nesbot/carbon source directly.
+     *
+     * @readonly
+     */
+    public bool $external = true;
+
     /**
      * @return class-string<T>
      */
@@ -31,20 +37,12 @@ trait CarbonTypeConverter
         return Carbon::class;
     }
 
-    /**
-     * @return string
-     */
-    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
+    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
     {
-        $precision = $fieldDeclaration['precision'] ?: 10;
-
-        if ($fieldDeclaration['secondPrecision'] ?? false) {
-            $precision = 0;
-        }
-
-        if ($precision === 10) {
-            $precision = DateTimeDefaultPrecision::get();
-        }
+        $precision = min(
+            $fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
+            $this->getMaximumPrecision($platform),
+        );
 
         $type = parent::getSQLDeclaration($fieldDeclaration, $platform);
 
@@ -63,10 +61,25 @@ trait CarbonTypeConverter
 
     /**
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
-     *
-     * @return T|null
      */
-    public function convertToPHPValue($value, AbstractPlatform $platform)
+    public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
+    {
+        if ($value === null) {
+            return $value;
+        }
+
+        if ($value instanceof DateTimeInterface) {
+            return $value->format('Y-m-d H:i:s.u');
+        }
+
+        throw InvalidType::new(
+            $value,
+            static::class,
+            ['null', 'DateTime', 'Carbon']
+        );
+    }
+
+    private function doConvertToPHPValue(mixed $value)
     {
         $class = $this->getCarbonClassName();
 
@@ -88,9 +101,9 @@ trait CarbonTypeConverter
         }
 
         if (!$date) {
-            throw ConversionException::conversionFailedFormat(
+            throw ValueNotConvertible::new(
                 $value,
-                $this->getName(),
+                static::class,
                 'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
                 $error
             );
@@ -99,25 +112,20 @@ trait CarbonTypeConverter
         return $date;
     }
 
-    /**
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
-     *
-     * @return string|null
-     */
-    public function convertToDatabaseValue($value, AbstractPlatform $platform)
+    private function getMaximumPrecision(AbstractPlatform $platform): int
     {
-        if ($value === null) {
-            return $value;
+        if ($platform instanceof DB2Platform) {
+            return 12;
         }
 
-        if ($value instanceof DateTimeInterface) {
-            return $value->format('Y-m-d H:i:s.u');
+        if ($platform instanceof OraclePlatform) {
+            return 9;
         }
 
-        throw ConversionException::conversionFailedInvalidType(
-            $value,
-            $this->getName(),
-            ['null', 'DateTime', 'Carbon']
-        );
+        if ($platform instanceof SQLServerPlatform || $platform instanceof SQLitePlatform) {
+            return 3;
+        }
+
+        return 6;
     }
 }

+ 1 - 8
data/web/inc/lib/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeDefaultPrecision.php → data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php

@@ -1,13 +1,6 @@
 <?php
 
-/**
- * This file is part of the Carbon package.
- *
- * (c) Brian Nesbitt <brian@nesbot.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+declare(strict_types=1);
 
 namespace Carbon\Doctrine;
 

+ 12 - 4
data/web/inc/lib/vendor/nesbot/carbon/src/Carbon/Doctrine/DateTimeImmutableType.php → data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php

@@ -1,12 +1,12 @@
 <?php
 
-/**
- * Thanks to https://github.com/flaushi for his suggestion:
- * https://github.com/doctrine/dbal/issues/2873#issuecomment-534956358
- */
+declare(strict_types=1);
+
 namespace Carbon\Doctrine;
 
 use Carbon\CarbonImmutable;
+use DateTimeImmutable;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
 use Doctrine\DBAL\Types\VarDateTimeImmutableType;
 
 class DateTimeImmutableType extends VarDateTimeImmutableType implements CarbonDoctrineType
@@ -14,6 +14,14 @@ class DateTimeImmutableType extends VarDateTimeImmutableType implements CarbonDo
     /** @use CarbonTypeConverter<CarbonImmutable> */
     use CarbonTypeConverter;
 
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonImmutable
+    {
+        return $this->doConvertToPHPValue($value);
+    }
+
     /**
      * @return class-string<CarbonImmutable>
      */

+ 24 - 0
data/web/inc/lib/vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php

@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Carbon\Doctrine;
+
+use Carbon\Carbon;
+use DateTime;
+use Doctrine\DBAL\Platforms\AbstractPlatform;
+use Doctrine\DBAL\Types\VarDateTimeType;
+
+class DateTimeType extends VarDateTimeType implements CarbonDoctrineType
+{
+    /** @use CarbonTypeConverter<Carbon> */
+    use CarbonTypeConverter;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Carbon
+    {
+        return $this->doConvertToPHPValue($value);
+    }
+}

+ 72 - 65
data/web/inc/lib/vendor/composer/ClassLoader.php

@@ -42,35 +42,37 @@ namespace Composer\Autoload;
  */
 class ClassLoader
 {
-    /** @var ?string */
+    /** @var \Closure(string):void */
+    private static $includeFile;
+
+    /** @var string|null */
     private $vendorDir;
 
     // PSR-4
     /**
-     * @var array[]
-     * @psalm-var array<string, array<string, int>>
+     * @var array<string, array<string, int>>
      */
     private $prefixLengthsPsr4 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, array<int, string>>
+     * @var array<string, list<string>>
      */
     private $prefixDirsPsr4 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, string>
+     * @var list<string>
      */
     private $fallbackDirsPsr4 = array();
 
     // PSR-0
     /**
-     * @var array[]
-     * @psalm-var array<string, array<string, string[]>>
+     * List of PSR-0 prefixes
+     *
+     * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+     *
+     * @var array<string, array<string, list<string>>>
      */
     private $prefixesPsr0 = array();
     /**
-     * @var array[]
-     * @psalm-var array<string, string>
+     * @var list<string>
      */
     private $fallbackDirsPsr0 = array();
 
@@ -78,8 +80,7 @@ class ClassLoader
     private $useIncludePath = false;
 
     /**
-     * @var string[]
-     * @psalm-var array<string, string>
+     * @var array<string, string>
      */
     private $classMap = array();
 
@@ -87,29 +88,29 @@ class ClassLoader
     private $classMapAuthoritative = false;
 
     /**
-     * @var bool[]
-     * @psalm-var array<string, bool>
+     * @var array<string, bool>
      */
     private $missingClasses = array();
 
-    /** @var ?string */
+    /** @var string|null */
     private $apcuPrefix;
 
     /**
-     * @var self[]
+     * @var array<string, self>
      */
     private static $registeredLoaders = array();
 
     /**
-     * @param ?string $vendorDir
+     * @param string|null $vendorDir
      */
     public function __construct($vendorDir = null)
     {
         $this->vendorDir = $vendorDir;
+        self::initializeIncludeClosure();
     }
 
     /**
-     * @return string[]
+     * @return array<string, list<string>>
      */
     public function getPrefixes()
     {
@@ -121,8 +122,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, array<int, string>>
+     * @return array<string, list<string>>
      */
     public function getPrefixesPsr4()
     {
@@ -130,8 +130,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, string>
+     * @return list<string>
      */
     public function getFallbackDirs()
     {
@@ -139,8 +138,7 @@ class ClassLoader
     }
 
     /**
-     * @return array[]
-     * @psalm-return array<string, string>
+     * @return list<string>
      */
     public function getFallbackDirsPsr4()
     {
@@ -148,8 +146,7 @@ class ClassLoader
     }
 
     /**
-     * @return string[] Array of classname => path
-     * @psalm-return array<string, string>
+     * @return array<string, string> Array of classname => path
      */
     public function getClassMap()
     {
@@ -157,8 +154,7 @@ class ClassLoader
     }
 
     /**
-     * @param string[] $classMap Class to filename map
-     * @psalm-param array<string, string> $classMap
+     * @param array<string, string> $classMap Class to filename map
      *
      * @return void
      */
@@ -175,24 +171,25 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix, either
      * appending or prepending to the ones previously set for this prefix.
      *
-     * @param string          $prefix  The prefix
-     * @param string[]|string $paths   The PSR-0 root directories
-     * @param bool            $prepend Whether to prepend the directories
+     * @param string              $prefix  The prefix
+     * @param list<string>|string $paths   The PSR-0 root directories
+     * @param bool                $prepend Whether to prepend the directories
      *
      * @return void
      */
     public function add($prefix, $paths, $prepend = false)
     {
+        $paths = (array) $paths;
         if (!$prefix) {
             if ($prepend) {
                 $this->fallbackDirsPsr0 = array_merge(
-                    (array) $paths,
+                    $paths,
                     $this->fallbackDirsPsr0
                 );
             } else {
                 $this->fallbackDirsPsr0 = array_merge(
                     $this->fallbackDirsPsr0,
-                    (array) $paths
+                    $paths
                 );
             }
 
@@ -201,19 +198,19 @@ class ClassLoader
 
         $first = $prefix[0];
         if (!isset($this->prefixesPsr0[$first][$prefix])) {
-            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+            $this->prefixesPsr0[$first][$prefix] = $paths;
 
             return;
         }
         if ($prepend) {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
-                (array) $paths,
+                $paths,
                 $this->prefixesPsr0[$first][$prefix]
             );
         } else {
             $this->prefixesPsr0[$first][$prefix] = array_merge(
                 $this->prefixesPsr0[$first][$prefix],
-                (array) $paths
+                $paths
             );
         }
     }
@@ -222,9 +219,9 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace, either
      * appending or prepending to the ones previously set for this namespace.
      *
-     * @param string          $prefix  The prefix/namespace, with trailing '\\'
-     * @param string[]|string $paths   The PSR-4 base directories
-     * @param bool            $prepend Whether to prepend the directories
+     * @param string              $prefix  The prefix/namespace, with trailing '\\'
+     * @param list<string>|string $paths   The PSR-4 base directories
+     * @param bool                $prepend Whether to prepend the directories
      *
      * @throws \InvalidArgumentException
      *
@@ -232,17 +229,18 @@ class ClassLoader
      */
     public function addPsr4($prefix, $paths, $prepend = false)
     {
+        $paths = (array) $paths;
         if (!$prefix) {
             // Register directories for the root namespace.
             if ($prepend) {
                 $this->fallbackDirsPsr4 = array_merge(
-                    (array) $paths,
+                    $paths,
                     $this->fallbackDirsPsr4
                 );
             } else {
                 $this->fallbackDirsPsr4 = array_merge(
                     $this->fallbackDirsPsr4,
-                    (array) $paths
+                    $paths
                 );
             }
         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
@@ -252,18 +250,18 @@ class ClassLoader
                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
             }
             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
-            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+            $this->prefixDirsPsr4[$prefix] = $paths;
         } elseif ($prepend) {
             // Prepend directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
-                (array) $paths,
+                $paths,
                 $this->prefixDirsPsr4[$prefix]
             );
         } else {
             // Append directories for an already registered namespace.
             $this->prefixDirsPsr4[$prefix] = array_merge(
                 $this->prefixDirsPsr4[$prefix],
-                (array) $paths
+                $paths
             );
         }
     }
@@ -272,8 +270,8 @@ class ClassLoader
      * Registers a set of PSR-0 directories for a given prefix,
      * replacing any others previously set for this prefix.
      *
-     * @param string          $prefix The prefix
-     * @param string[]|string $paths  The PSR-0 base directories
+     * @param string              $prefix The prefix
+     * @param list<string>|string $paths  The PSR-0 base directories
      *
      * @return void
      */
@@ -290,8 +288,8 @@ class ClassLoader
      * Registers a set of PSR-4 directories for a given namespace,
      * replacing any others previously set for this namespace.
      *
-     * @param string          $prefix The prefix/namespace, with trailing '\\'
-     * @param string[]|string $paths  The PSR-4 base directories
+     * @param string              $prefix The prefix/namespace, with trailing '\\'
+     * @param list<string>|string $paths  The PSR-4 base directories
      *
      * @throws \InvalidArgumentException
      *
@@ -425,7 +423,8 @@ class ClassLoader
     public function loadClass($class)
     {
         if ($file = $this->findFile($class)) {
-            includeFile($file);
+            $includeFile = self::$includeFile;
+            $includeFile($file);
 
             return true;
         }
@@ -476,9 +475,9 @@ class ClassLoader
     }
 
     /**
-     * Returns the currently registered loaders indexed by their corresponding vendor directories.
+     * Returns the currently registered loaders keyed by their corresponding vendor directories.
      *
-     * @return self[]
+     * @return array<string, self>
      */
     public static function getRegisteredLoaders()
     {
@@ -555,18 +554,26 @@ class ClassLoader
 
         return false;
     }
-}
 
-/**
- * Scope isolated include.
- *
- * Prevents access to $this/self from included files.
- *
- * @param  string $file
- * @return void
- * @private
- */
-function includeFile($file)
-{
-    include $file;
+    /**
+     * @return void
+     */
+    private static function initializeIncludeClosure()
+    {
+        if (self::$includeFile !== null) {
+            return;
+        }
+
+        /**
+         * Scope isolated include.
+         *
+         * Prevents access to $this/self from included files.
+         *
+         * @param  string $file
+         * @return void
+         */
+        self::$includeFile = \Closure::bind(static function($file) {
+            include $file;
+        }, null, null);
+    }
 }

+ 12 - 5
data/web/inc/lib/vendor/composer/InstalledVersions.php

@@ -98,7 +98,7 @@ class InstalledVersions
     {
         foreach (self::getInstalled() as $installed) {
             if (isset($installed['versions'][$packageName])) {
-                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
+                return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
             }
         }
 
@@ -119,7 +119,7 @@ class InstalledVersions
      */
     public static function satisfies(VersionParser $parser, $packageName, $constraint)
     {
-        $constraint = $parser->parseConstraints($constraint);
+        $constraint = $parser->parseConstraints((string) $constraint);
         $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
 
         return $provided->matches($constraint);
@@ -328,7 +328,9 @@ class InstalledVersions
                 if (isset(self::$installedByVendor[$vendorDir])) {
                     $installed[] = self::$installedByVendor[$vendorDir];
                 } elseif (is_file($vendorDir.'/composer/installed.php')) {
-                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
+                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+                    $required = require $vendorDir.'/composer/installed.php';
+                    $installed[] = self::$installedByVendor[$vendorDir] = $required;
                     if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
                         self::$installed = $installed[count($installed) - 1];
                     }
@@ -340,12 +342,17 @@ class InstalledVersions
             // only require the installed.php file if this file is loaded from its dumped location,
             // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
             if (substr(__DIR__, -8, 1) !== 'C') {
-                self::$installed = require __DIR__ . '/installed.php';
+                /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+                $required = require __DIR__ . '/installed.php';
+                self::$installed = $required;
             } else {
                 self::$installed = array();
             }
         }
-        $installed[] = self::$installed;
+
+        if (self::$installed !== array()) {
+            $installed[] = self::$installed;
+        }
 
         return $installed;
     }

+ 1 - 0
data/web/inc/lib/vendor/composer/autoload_classmap.php

@@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir);
 return array(
     'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
     'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+    'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
     'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
     'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
     'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',

+ 2 - 2
data/web/inc/lib/vendor/composer/autoload_files.php

@@ -6,12 +6,12 @@ $vendorDir = dirname(__DIR__);
 $baseDir = dirname($vendorDir);
 
 return array(
+    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
     '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
     '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
     'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
-    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
-    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
     'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
     'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
     '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
     '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',

+ 2 - 0
data/web/inc/lib/vendor/composer/autoload_psr4.php

@@ -21,6 +21,7 @@ return array(
     'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
     'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
     'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
+    'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
     'PhpMimeMailParser\\' => array($vendorDir . '/php-mime-mail-parser/php-mime-mail-parser/src'),
     'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
     'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
@@ -34,5 +35,6 @@ return array(
     'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
     'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
     'Ddeboer\\Imap\\' => array($vendorDir . '/ddeboer/imap/src'),
+    'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
     'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
 );

+ 10 - 17
data/web/inc/lib/vendor/composer/autoload_real.php

@@ -33,25 +33,18 @@ class ComposerAutoloaderInit873464e4bd965a3168f133248b1b218b
 
         $loader->register(true);
 
-        $includeFiles = \Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
-        foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file);
+        $filesToLoad = \Composer\Autoload\ComposerStaticInit873464e4bd965a3168f133248b1b218b::$files;
+        $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
+            if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+                $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+                require $file;
+            }
+        }, null, null);
+        foreach ($filesToLoad as $fileIdentifier => $file) {
+            $requireFile($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
-
-/**
- * @param string $fileIdentifier
- * @param string $file
- * @return void
- */
-function composerRequire873464e4bd965a3168f133248b1b218b($fileIdentifier, $file)
-{
-    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
-        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
-
-        require $file;
-    }
-}

+ 13 - 2
data/web/inc/lib/vendor/composer/autoload_static.php

@@ -7,12 +7,12 @@ namespace Composer\Autoload;
 class ComposerStaticInit873464e4bd965a3168f133248b1b218b
 {
     public static $files = array (
+        '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
         '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
         'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
-        '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
-        '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
         'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
+        '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
         'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
         '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
         '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
@@ -48,6 +48,7 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
             'Psr\\Http\\Message\\' => 17,
             'Psr\\Http\\Client\\' => 16,
             'Psr\\Container\\' => 14,
+            'Psr\\Clock\\' => 10,
             'PhpMimeMailParser\\' => 18,
             'PHPMailer\\PHPMailer\\' => 20,
         ),
@@ -85,6 +86,7 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
         ),
         'C' => 
         array (
+            'Carbon\\Doctrine\\' => 16,
             'Carbon\\' => 7,
         ),
     );
@@ -151,6 +153,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
         array (
             0 => __DIR__ . '/..' . '/psr/container/src',
         ),
+        'Psr\\Clock\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/clock/src',
+        ),
         'PhpMimeMailParser\\' => 
         array (
             0 => __DIR__ . '/..' . '/php-mime-mail-parser/php-mime-mail-parser/src',
@@ -203,6 +209,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
         array (
             0 => __DIR__ . '/..' . '/ddeboer/imap/src',
         ),
+        'Carbon\\Doctrine\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine',
+        ),
         'Carbon\\' => 
         array (
             0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
@@ -222,6 +232,7 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
     public static $classMap = array (
         'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
         'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+        'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
         'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
         'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
         'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',

+ 245 - 119
data/web/inc/lib/vendor/composer/installed.json

@@ -61,6 +61,78 @@
             ],
             "install-path": "../bshaffer/oauth2-server-php"
         },
+        {
+            "name": "carbonphp/carbon-doctrine-types",
+            "version": "3.2.0",
+            "version_normalized": "3.2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.1"
+            },
+            "conflict": {
+                "doctrine/dbal": "<4.0.0 || >=5.0.0"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^4.0.0",
+                "nesbot/carbon": "^2.71.0 || ^3.0.0",
+                "phpunit/phpunit": "^10.3"
+            },
+            "time": "2024-02-09T16:56:22+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "KyleKatarn",
+                    "email": "kylekatarnls@gmail.com"
+                }
+            ],
+            "description": "Types to use Carbon in Doctrine",
+            "keywords": [
+                "carbon",
+                "date",
+                "datetime",
+                "doctrine",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues",
+                "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/kylekatarnls",
+                    "type": "github"
+                },
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../carbonphp/carbon-doctrine-types"
+        },
         {
             "name": "ddeboer/imap",
             "version": "1.13.1",
@@ -141,35 +213,36 @@
         },
         {
             "name": "directorytree/ldaprecord",
-            "version": "v2.10.1",
-            "version_normalized": "2.10.1.0",
+            "version": "v2.20.5",
+            "version_normalized": "2.20.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/DirectoryTree/LdapRecord.git",
-                "reference": "bf512d9af7a7b0e2ed7a666ab29cefdd027bee88"
+                "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/bf512d9af7a7b0e2ed7a666ab29cefdd027bee88",
-                "reference": "bf512d9af7a7b0e2ed7a666ab29cefdd027bee88",
+                "url": "https://api.github.com/repos/DirectoryTree/LdapRecord/zipball/5bd0a5a9d257cf1049ae83055dbba4c3479ddf16",
+                "reference": "5bd0a5a9d257cf1049ae83055dbba4c3479ddf16",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "ext-ldap": "*",
-                "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
+                "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0",
                 "nesbot/carbon": "^1.0|^2.0",
                 "php": ">=7.3",
-                "psr/log": "*",
+                "psr/log": "^1.0|^2.0|^3.0",
                 "psr/simple-cache": "^1.0|^2.0",
-                "tightenco/collect": "^5.6|^6.0|^7.0|^8.0"
+                "symfony/polyfill-php80": "^1.25",
+                "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0"
             },
             "require-dev": {
                 "mockery/mockery": "^1.0",
                 "phpunit/phpunit": "^9.0",
                 "spatie/ray": "^1.24"
             },
-            "time": "2022-02-25T16:00:51+00:00",
+            "time": "2023-10-11T16:34:34+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -620,29 +693,29 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v9.3.0",
-            "version_normalized": "9.3.0.0",
+            "version": "v10.44.0",
+            "version_normalized": "10.44.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
-                "reference": "bf4b3c254c49d28157645d01e4883b5951b1e1d0"
+                "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/contracts/zipball/bf4b3c254c49d28157645d01e4883b5951b1e1d0",
-                "reference": "bf4b3c254c49d28157645d01e4883b5951b1e1d0",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
+                "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac",
                 "shasum": ""
             },
             "require": {
-                "php": "^8.0.2",
+                "php": "^8.1",
                 "psr/container": "^1.1.1|^2.0.1",
                 "psr/simple-cache": "^1.0|^2.0|^3.0"
             },
-            "time": "2022-02-22T14:45:39+00:00",
+            "time": "2024-01-15T18:52:32+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "9.x-dev"
+                    "dev-master": "10.x-dev"
                 }
             },
             "installation-source": "dist",
@@ -930,38 +1003,45 @@
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.57.0",
-            "version_normalized": "2.57.0.0",
+            "version": "2.72.3",
+            "version_normalized": "2.72.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "4a54375c21eea4811dbd1149fe6b246517554e78"
+                "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4a54375c21eea4811dbd1149fe6b246517554e78",
-                "reference": "4a54375c21eea4811dbd1149fe6b246517554e78",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/0c6fd108360c562f6e4fd1dedb8233b423e91c83",
+                "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83",
                 "shasum": ""
             },
             "require": {
+                "carbonphp/carbon-doctrine-types": "*",
                 "ext-json": "*",
                 "php": "^7.1.8 || ^8.0",
+                "psr/clock": "^1.0",
                 "symfony/polyfill-mbstring": "^1.0",
                 "symfony/polyfill-php80": "^1.16",
                 "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0"
             },
+            "provide": {
+                "psr/clock-implementation": "1.0"
+            },
             "require-dev": {
-                "doctrine/dbal": "^2.0 || ^3.0",
-                "doctrine/orm": "^2.7",
+                "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0",
+                "doctrine/orm": "^2.7 || ^3.0",
                 "friendsofphp/php-cs-fixer": "^3.0",
                 "kylekatarnls/multi-tester": "^2.0",
+                "ondrejmirtes/better-reflection": "*",
                 "phpmd/phpmd": "^2.9",
                 "phpstan/extension-installer": "^1.0",
-                "phpstan/phpstan": "^0.12.54 || ^1.0",
-                "phpunit/phpunit": "^7.5.20 || ^8.5.14",
+                "phpstan/phpstan": "^0.12.99 || ^1.7.14",
+                "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20",
                 "squizlabs/php_codesniffer": "^3.4"
             },
-            "time": "2022-02-13T18:13:33+00:00",
+            "time": "2024-01-25T10:35:09+00:00",
             "bin": [
                 "bin/carbon"
             ],
@@ -1017,11 +1097,15 @@
             },
             "funding": [
                 {
-                    "url": "https://opencollective.com/Carbon",
-                    "type": "open_collective"
+                    "url": "https://github.com/sponsors/kylekatarnls",
+                    "type": "github"
                 },
                 {
-                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "url": "https://opencollective.com/Carbon#sponsor",
+                    "type": "opencollective"
+                },
+                {
+                    "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme",
                     "type": "tidelift"
                 }
             ],
@@ -1255,6 +1339,57 @@
             ],
             "install-path": "../phpmailer/phpmailer"
         },
+        {
+            "name": "psr/clock",
+            "version": "1.0.0",
+            "version_normalized": "1.0.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/clock.git",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0"
+            },
+            "time": "2022-11-25T14:36:26+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Clock\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for reading the clock.",
+            "homepage": "https://github.com/php-fig/clock",
+            "keywords": [
+                "clock",
+                "now",
+                "psr",
+                "psr-20",
+                "time"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/clock/issues",
+                "source": "https://github.com/php-fig/clock/tree/1.0.0"
+            },
+            "install-path": "../psr/clock"
+        },
         {
             "name": "psr/container",
             "version": "2.0.2",
@@ -1826,27 +1961,27 @@
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v3.2.1",
-            "version_normalized": "3.2.1.0",
+            "version": "v3.4.0",
+            "version_normalized": "3.4.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
+                "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
-                "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
+                "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
                 "shasum": ""
             },
             "require": {
                 "php": ">=8.1"
             },
-            "time": "2023-03-01T10:25:55+00:00",
+            "time": "2023-05-23T14:45:45+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "3.3-dev"
+                    "dev-main": "3.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -1876,7 +2011,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
             },
             "funding": [
                 {
@@ -1981,17 +2116,17 @@
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.24.0",
-            "version_normalized": "1.24.0.0",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
-                "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+                "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
                 "shasum": ""
             },
             "require": {
@@ -2003,12 +2138,9 @@
             "suggest": {
                 "ext-mbstring": "For best performance"
             },
-            "time": "2021-11-30T18:21:41+00:00",
+            "time": "2024-01-29T20:11:03+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.23-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -2047,7 +2179,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -2067,28 +2199,25 @@
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.24.0",
-            "version_normalized": "1.24.0.0",
+            "version": "v1.29.0",
+            "version_normalized": "1.29.0.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9"
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9",
-                "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+                "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.1"
             },
-            "time": "2021-09-13T13:58:33+00:00",
+            "time": "2024-01-29T20:11:03+00:00",
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-main": "1.23-dev"
-                },
                 "thanks": {
                     "name": "symfony/polyfill",
                     "url": "https://github.com/symfony/polyfill"
@@ -2133,7 +2262,7 @@
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0"
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
             },
             "funding": [
                 {
@@ -2153,29 +2282,32 @@
         },
         {
             "name": "symfony/translation",
-            "version": "v6.0.5",
-            "version_normalized": "6.0.5.0",
+            "version": "v6.4.3",
+            "version_normalized": "6.4.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875"
+                "reference": "637c51191b6b184184bbf98937702bcf554f7d04"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/e69501c71107cc3146b32aaa45f4edd0c3427875",
-                "reference": "e69501c71107cc3146b32aaa45f4edd0c3427875",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/637c51191b6b184184bbf98937702bcf554f7d04",
+                "reference": "637c51191b6b184184bbf98937702bcf554f7d04",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2",
+                "php": ">=8.1",
+                "symfony/deprecation-contracts": "^2.5|^3",
                 "symfony/polyfill-mbstring": "~1.0",
-                "symfony/translation-contracts": "^2.3|^3.0"
+                "symfony/translation-contracts": "^2.5|^3.0"
             },
             "conflict": {
                 "symfony/config": "<5.4",
                 "symfony/console": "<5.4",
                 "symfony/dependency-injection": "<5.4",
+                "symfony/http-client-contracts": "<2.5",
                 "symfony/http-kernel": "<5.4",
+                "symfony/service-contracts": "<2.5",
                 "symfony/twig-bundle": "<5.4",
                 "symfony/yaml": "<5.4"
             },
@@ -2183,24 +2315,21 @@
                 "symfony/translation-implementation": "2.3|3.0"
             },
             "require-dev": {
+                "nikic/php-parser": "^4.18|^5.0",
                 "psr/log": "^1|^2|^3",
-                "symfony/config": "^5.4|^6.0",
-                "symfony/console": "^5.4|^6.0",
-                "symfony/dependency-injection": "^5.4|^6.0",
-                "symfony/finder": "^5.4|^6.0",
-                "symfony/http-client-contracts": "^1.1|^2.0|^3.0",
-                "symfony/http-kernel": "^5.4|^6.0",
-                "symfony/intl": "^5.4|^6.0",
+                "symfony/config": "^5.4|^6.0|^7.0",
+                "symfony/console": "^5.4|^6.0|^7.0",
+                "symfony/dependency-injection": "^5.4|^6.0|^7.0",
+                "symfony/finder": "^5.4|^6.0|^7.0",
+                "symfony/http-client-contracts": "^2.5|^3.0",
+                "symfony/http-kernel": "^5.4|^6.0|^7.0",
+                "symfony/intl": "^5.4|^6.0|^7.0",
                 "symfony/polyfill-intl-icu": "^1.21",
-                "symfony/service-contracts": "^1.1.2|^2|^3",
-                "symfony/yaml": "^5.4|^6.0"
-            },
-            "suggest": {
-                "psr/log-implementation": "To use logging capability in translator",
-                "symfony/config": "",
-                "symfony/yaml": ""
+                "symfony/routing": "^5.4|^6.0|^7.0",
+                "symfony/service-contracts": "^2.5|^3",
+                "symfony/yaml": "^5.4|^6.0|^7.0"
             },
-            "time": "2022-02-09T15:52:48+00:00",
+            "time": "2024-01-29T13:11:52+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -2231,7 +2360,7 @@
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v6.0.5"
+                "source": "https://github.com/symfony/translation/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2251,30 +2380,27 @@
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v3.0.0",
-            "version_normalized": "3.0.0.0",
+            "version": "v3.4.1",
+            "version_normalized": "3.4.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77"
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1b6ea5a7442af5a12dba3dbd6d71034b5b234e77",
-                "reference": "1b6ea5a7442af5a12dba3dbd6d71034b5b234e77",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7",
+                "reference": "06450585bf65e978026bda220cdebca3f867fde7",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2"
-            },
-            "suggest": {
-                "symfony/translation-implementation": ""
+                "php": ">=8.1"
             },
-            "time": "2021-09-07T12:43:40+00:00",
+            "time": "2023-12-26T14:02:43+00:00",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "3.0-dev"
+                    "dev-main": "3.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -2285,7 +2411,10 @@
             "autoload": {
                 "psr-4": {
                     "Symfony\\Contracts\\Translation\\": ""
-                }
+                },
+                "exclude-from-classmap": [
+                    "/Test/"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -2312,7 +2441,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v3.0.0"
+                "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1"
             },
             "funding": [
                 {
@@ -2332,40 +2461,37 @@
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v6.0.5",
-            "version_normalized": "6.0.5.0",
+            "version": "v6.4.3",
+            "version_normalized": "6.4.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce"
+                "reference": "0435a08f69125535336177c29d56af3abc1f69da"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/60d6a756d5f485df5e6e40b337334848f79f61ce",
-                "reference": "60d6a756d5f485df5e6e40b337334848f79f61ce",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0435a08f69125535336177c29d56af3abc1f69da",
+                "reference": "0435a08f69125535336177c29d56af3abc1f69da",
                 "shasum": ""
             },
             "require": {
-                "php": ">=8.0.2",
+                "php": ">=8.1",
+                "symfony/deprecation-contracts": "^2.5|^3",
                 "symfony/polyfill-mbstring": "~1.0"
             },
             "conflict": {
-                "phpunit/phpunit": "<5.4.3",
                 "symfony/console": "<5.4"
             },
             "require-dev": {
                 "ext-iconv": "*",
-                "symfony/console": "^5.4|^6.0",
-                "symfony/process": "^5.4|^6.0",
-                "symfony/uid": "^5.4|^6.0",
+                "symfony/console": "^5.4|^6.0|^7.0",
+                "symfony/error-handler": "^6.3|^7.0",
+                "symfony/http-kernel": "^5.4|^6.0|^7.0",
+                "symfony/process": "^5.4|^6.0|^7.0",
+                "symfony/uid": "^5.4|^6.0|^7.0",
                 "twig/twig": "^2.13|^3.0.4"
             },
-            "suggest": {
-                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
-                "ext-intl": "To show region name in time zone dump",
-                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
-            },
-            "time": "2022-02-21T17:15:17+00:00",
+            "time": "2024-01-23T14:53:30+00:00",
             "bin": [
                 "Resources/bin/var-dump-server"
             ],
@@ -2403,7 +2529,7 @@
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v6.0.5"
+                "source": "https://github.com/symfony/var-dumper/tree/v6.4.3"
             },
             "funding": [
                 {
@@ -2423,21 +2549,21 @@
         },
         {
             "name": "tightenco/collect",
-            "version": "v8.83.2",
-            "version_normalized": "8.83.2.0",
+            "version": "v9.52.7",
+            "version_normalized": "9.52.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tighten/collect.git",
-                "reference": "d9c66d586ec2d216d8a31283d73f8df1400cc722"
+                "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tighten/collect/zipball/d9c66d586ec2d216d8a31283d73f8df1400cc722",
-                "reference": "d9c66d586ec2d216d8a31283d73f8df1400cc722",
+                "url": "https://api.github.com/repos/tighten/collect/zipball/b15143cd11fe01a700fcc449df61adc64452fa6d",
+                "reference": "b15143cd11fe01a700fcc449df61adc64452fa6d",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.3|^8.0",
+                "php": "^8.0",
                 "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0 || ^6.0"
             },
             "require-dev": {
@@ -2445,7 +2571,7 @@
                 "nesbot/carbon": "^2.23.0",
                 "phpunit/phpunit": "^8.3"
             },
-            "time": "2022-02-16T16:15:54+00:00",
+            "time": "2023-04-14T21:51:36+00:00",
             "type": "library",
             "installation-source": "dist",
             "autoload": {
@@ -2474,7 +2600,7 @@
             ],
             "support": {
                 "issues": "https://github.com/tighten/collect/issues",
-                "source": "https://github.com/tighten/collect/tree/v8.83.2"
+                "source": "https://github.com/tighten/collect/tree/v9.52.7"
             },
             "install-path": "../tightenco/collect"
         },

+ 56 - 32
data/web/inc/lib/vendor/composer/installed.php

@@ -3,7 +3,7 @@
         'name' => '__root__',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => '96390c2e12fd8d886495fde5514ad431e4e66069',
+        'reference' => '40146839efb3754b2db4045f0111178ffd1883c5',
         'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -13,7 +13,7 @@
         '__root__' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => '96390c2e12fd8d886495fde5514ad431e4e66069',
+            'reference' => '40146839efb3754b2db4045f0111178ffd1883c5',
             'type' => 'library',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
@@ -28,6 +28,15 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'carbonphp/carbon-doctrine-types' => array(
+            'pretty_version' => '3.2.0',
+            'version' => '3.2.0.0',
+            'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
         'ddeboer/imap' => array(
             'pretty_version' => '1.13.1',
             'version' => '1.13.1.0',
@@ -38,9 +47,9 @@
             'dev_requirement' => false,
         ),
         'directorytree/ldaprecord' => array(
-            'pretty_version' => 'v2.10.1',
-            'version' => '2.10.1.0',
-            'reference' => 'bf512d9af7a7b0e2ed7a666ab29cefdd027bee88',
+            'pretty_version' => 'v2.20.5',
+            'version' => '2.20.5.0',
+            'reference' => '5bd0a5a9d257cf1049ae83055dbba4c3479ddf16',
             'type' => 'library',
             'install_path' => __DIR__ . '/../directorytree/ldaprecord',
             'aliases' => array(),
@@ -89,9 +98,9 @@
             'dev_requirement' => false,
         ),
         'illuminate/contracts' => array(
-            'pretty_version' => 'v9.3.0',
-            'version' => '9.3.0.0',
-            'reference' => 'bf4b3c254c49d28157645d01e4883b5951b1e1d0',
+            'pretty_version' => 'v10.44.0',
+            'version' => '10.44.0.0',
+            'reference' => '8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac',
             'type' => 'library',
             'install_path' => __DIR__ . '/../illuminate/contracts',
             'aliases' => array(),
@@ -140,9 +149,9 @@
             'dev_requirement' => false,
         ),
         'nesbot/carbon' => array(
-            'pretty_version' => '2.57.0',
-            'version' => '2.57.0.0',
-            'reference' => '4a54375c21eea4811dbd1149fe6b246517554e78',
+            'pretty_version' => '2.72.3',
+            'version' => '2.72.3.0',
+            'reference' => '0c6fd108360c562f6e4fd1dedb8233b423e91c83',
             'type' => 'library',
             'install_path' => __DIR__ . '/../nesbot/carbon',
             'aliases' => array(),
@@ -175,6 +184,21 @@
             'aliases' => array(),
             'dev_requirement' => false,
         ),
+        'psr/clock' => array(
+            'pretty_version' => '1.0.0',
+            'version' => '1.0.0.0',
+            'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/clock',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/clock-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
         'psr/container' => array(
             'pretty_version' => '2.0.2',
             'version' => '2.0.2.0',
@@ -284,9 +308,9 @@
             'dev_requirement' => false,
         ),
         'symfony/deprecation-contracts' => array(
-            'pretty_version' => 'v3.2.1',
-            'version' => '3.2.1.0',
-            'reference' => 'e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e',
+            'pretty_version' => 'v3.4.0',
+            'version' => '3.4.0.0',
+            'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
             'aliases' => array(),
@@ -302,36 +326,36 @@
             'dev_requirement' => false,
         ),
         'symfony/polyfill-mbstring' => array(
-            'pretty_version' => 'v1.24.0',
-            'version' => '1.24.0.0',
-            'reference' => '0abb51d2f102e00a4eefcf46ba7fec406d245825',
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/polyfill-php80' => array(
-            'pretty_version' => 'v1.24.0',
-            'version' => '1.24.0.0',
-            'reference' => '57b712b08eddb97c762a8caa32c84e037892d2e9',
+            'pretty_version' => 'v1.29.0',
+            'version' => '1.29.0.0',
+            'reference' => '87b68208d5c1188808dd7839ee1e6c8ec3b02f1b',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/polyfill-php80',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/translation' => array(
-            'pretty_version' => 'v6.0.5',
-            'version' => '6.0.5.0',
-            'reference' => 'e69501c71107cc3146b32aaa45f4edd0c3427875',
+            'pretty_version' => 'v6.4.3',
+            'version' => '6.4.3.0',
+            'reference' => '637c51191b6b184184bbf98937702bcf554f7d04',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/translation',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'symfony/translation-contracts' => array(
-            'pretty_version' => 'v3.0.0',
-            'version' => '3.0.0.0',
-            'reference' => '1b6ea5a7442af5a12dba3dbd6d71034b5b234e77',
+            'pretty_version' => 'v3.4.1',
+            'version' => '3.4.1.0',
+            'reference' => '06450585bf65e978026bda220cdebca3f867fde7',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/translation-contracts',
             'aliases' => array(),
@@ -344,18 +368,18 @@
             ),
         ),
         'symfony/var-dumper' => array(
-            'pretty_version' => 'v6.0.5',
-            'version' => '6.0.5.0',
-            'reference' => '60d6a756d5f485df5e6e40b337334848f79f61ce',
+            'pretty_version' => 'v6.4.3',
+            'version' => '6.4.3.0',
+            'reference' => '0435a08f69125535336177c29d56af3abc1f69da',
             'type' => 'library',
             'install_path' => __DIR__ . '/../symfony/var-dumper',
             'aliases' => array(),
             'dev_requirement' => false,
         ),
         'tightenco/collect' => array(
-            'pretty_version' => 'v8.83.2',
-            'version' => '8.83.2.0',
-            'reference' => 'd9c66d586ec2d216d8a31283d73f8df1400cc722',
+            'pretty_version' => 'v9.52.7',
+            'version' => '9.52.7.0',
+            'reference' => 'b15143cd11fe01a700fcc449df61adc64452fa6d',
             'type' => 'library',
             'install_path' => __DIR__ . '/../tightenco/collect',
             'aliases' => array(),

+ 4 - 1
data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/bug_report.md

@@ -7,7 +7,10 @@ assignees: ''
 
 ---
 
-<!-- Please update the below information with your environment. -->
+<!--
+  Please update the below information with your environment.
+  Issues filed without the below information will be closed.
+-->
 **Environment:**
  - LDAP Server Type: [e.g. ActiveDirectory / OpenLDAP / FreeIPA]
  - PHP Version: [e.g. 7.3 / 7.4 / 8.0]

+ 4 - 1
data/web/inc/lib/vendor/directorytree/ldaprecord/.github/ISSUE_TEMPLATE/support---help-request.md

@@ -11,7 +11,10 @@ assignees: ''
 <!-- https://github.com/sponsors/stevebauman -->
 <!-- Thank you for your understanding. -->
 
-<!-- Please update the below information with your environment. -->
+<!--
+  Please update the below information with your environment.
+  Issues filed without the below information will be closed.
+-->
 **Environment:**
  - LDAP Server Type: [e.g. ActiveDirectory / OpenLDAP / FreeIPA]
  - PHP Version: [e.g. 7.3 / 7.4 / 8.0]

+ 62 - 0
data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-integration-tests.yml

@@ -0,0 +1,62 @@
+name: run-integration-tests
+
+on:
+  push:
+  pull_request:
+  schedule:
+    - cron: "0 0 * * *"
+
+jobs:
+  run-tests:
+    runs-on: ${{ matrix.os }}
+
+    services:
+      ldap:
+        image: osixia/openldap:1.4.0
+        env:
+          LDAP_TLS_VERIFY_CLIENT: try
+          LDAP_OPENLDAP_UID: 1000
+          LDAP_OPENLDAP_GID: 1000
+          LDAP_ORGANISATION: Local
+          LDAP_DOMAIN: local.com
+          LDAP_ADMIN_PASSWORD: secret
+        ports:
+          - 389:389
+          - 636:636
+
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        php: [8.1, 8.0, 7.4]
+
+    name: ${{ matrix.os }} - P${{ matrix.php }}
+
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+
+      - name: Cache dependencies
+        uses: actions/cache@v2
+        with:
+          path: ~/.composer/cache/files
+          key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
+
+      - name: Set ldap.conf file permissions
+        run: sudo chown -R $USER:$USER /etc/ldap/ldap.conf
+
+      - name: Create ldap.conf file disabling TLS verification
+        run: sudo echo "TLS_REQCERT never" > "/etc/ldap/ldap.conf"
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.php }}
+          extensions: ldap, json
+          coverage: none
+
+      - name: Install dependencies
+        run: composer update --prefer-dist --no-interaction
+
+      - name: Execute tests
+        run: vendor/bin/phpunit --testsuite Integration

+ 5 - 42
data/web/inc/lib/vendor/directorytree/ldaprecord/.github/workflows/run-tests.yml

@@ -9,20 +9,20 @@ on:
 jobs:
   run-tests:
     runs-on: ${{ matrix.os }}
+    name: ${{ matrix.os }} - P${{ matrix.php }}
+
     strategy:
       fail-fast: false
       matrix:
         os: [ubuntu-latest, windows-latest]
         php: [8.1, 8.0, 7.4, 7.3]
 
-    name: ${{ matrix.os }} - P${{ matrix.php }}
-
     steps:
       - name: Checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v2
 
       - name: Cache dependencies
-        uses: actions/cache@v3
+        uses: actions/cache@v2
         with:
           path: ~/.composer/cache/files
           key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
@@ -38,41 +38,4 @@ jobs:
         run: composer update --prefer-dist --no-interaction
 
       - name: Execute tests
-        run: vendor/bin/phpunit
-
-  run-analysis:
-    runs-on: ${{ matrix.os }}
-    name: Static code analysis (PHP ${{ matrix.php }})
-
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-latest]
-        php: [8.0]
-
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v4
-
-      - name: Cache dependencies
-        uses: actions/cache@v3
-        with:
-          path: ~/.composer/cache/files
-          key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
-
-      - name: Setup PHP
-        uses: shivammathur/setup-php@v2
-        with:
-          php-version: ${{ matrix.php }}
-          extensions: ldap, json
-          coverage: none
-          tools: psalm
-
-      - name: Validate composer.json
-        run: composer validate
-
-      - name: Install dependencies
-        run: composer update --prefer-dist --no-interaction
-
-      - name: Run Psalm
-        run: psalm
+        run: vendor/bin/phpunit --testsuite Unit

+ 0 - 7
data/web/inc/lib/vendor/directorytree/ldaprecord/.styleci.yml

@@ -1,8 +1 @@
 preset: laravel
-enabled:
-  - phpdoc_align
-  - phpdoc_separation
-  - unalign_double_arrow
-disabled:
-  - laravel_phpdoc_alignment
-  - laravel_phpdoc_separation

+ 4 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/composer.json

@@ -32,11 +32,12 @@
         "php": ">=7.3",
         "ext-ldap": "*",
         "ext-json": "*",
-        "psr/log": "*",
+        "psr/log": "^1.0|^2.0|^3.0",
         "psr/simple-cache": "^1.0|^2.0",
         "nesbot/carbon": "^1.0|^2.0",
-        "tightenco/collect": "^5.6|^6.0|^7.0|^8.0",
-        "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0"
+        "tightenco/collect": "^5.6|^6.0|^7.0|^8.0|^9.0",
+        "illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0",
+        "symfony/polyfill-php80": "^1.25"
     },
     "require-dev": {
         "phpunit/phpunit": "^9.0",

+ 35 - 0
data/web/inc/lib/vendor/directorytree/ldaprecord/docker-compose.yml

@@ -0,0 +1,35 @@
+version: '3'
+
+services:
+    ldap:
+        image: osixia/openldap:1.4.0
+        container_name: ldap
+        restart: always
+        hostname: local.com
+        environment:
+            LDAP_TLS_VERIFY_CLIENT: try
+            LDAP_OPENLDAP_UID: 1000
+            LDAP_OPENLDAP_GID: 1000
+            LDAP_ORGANISATION: Local
+            LDAP_DOMAIN : local.com
+            LDAP_ADMIN_PASSWORD: secret
+        ports:
+            - "389:389"
+            - "636:636"
+        networks:
+            - local
+  
+    ldapadmin:
+        image: osixia/phpldapadmin:0.9.0
+        container_name: ldapadmin
+        environment:
+            PHPLDAPADMIN_LDAP_HOSTS: ldap
+        restart: always
+        ports:
+            - "6443:443"
+        networks:
+            - local
+
+networks:
+    local:
+        driver: bridge

+ 5 - 2
data/web/inc/lib/vendor/directorytree/ldaprecord/phpunit.xml

@@ -10,8 +10,11 @@
          stopOnFailure="false"
         >
     <testsuites>
-        <testsuite name="LdapRecord Test Suite">
-            <directory suffix="Test.php">./tests/</directory>
+        <testsuite name="Unit">
+            <directory suffix="Test.php">./tests/Unit</directory>
+        </testsuite>
+        <testsuite name="Integration">
+            <directory suffix="Test.php">./tests/Integration</directory>
         </testsuite>
     </testsuites>
 </phpunit>

+ 0 - 15
data/web/inc/lib/vendor/directorytree/ldaprecord/psalm.xml

@@ -1,15 +0,0 @@
-<?xml version="1.0"?>
-<psalm
-    errorLevel="7"
-    resolveFromConfigFile="true"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xmlns="https://getpsalm.org/schema/config"
-    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
->
-    <projectFiles>
-        <directory name="src" />
-        <ignoreFiles>
-            <directory name="vendor" />
-        </ignoreFiles>
-    </projectFiles>
-</psalm>

+ 2 - 2
data/web/inc/lib/vendor/directorytree/ldaprecord/readme.md

@@ -6,7 +6,7 @@
 
 <p align="center">
     <a href="https://github.com/DirectoryTree/LdapRecord/actions">
-        <img src="https://img.shields.io/github/workflow/status/directorytree/ldaprecord/run-tests.svg?style=flat-square">
+        <img src="https://img.shields.io/github/actions/workflow/status/directorytree/ldaprecord/run-tests.yml?branch=master&style=flat-square">
     </a>
     <a href="https://scrutinizer-ci.com/g/DirectoryTree/LdapRecord/?branch=master">
         <img src="https://img.shields.io/scrutinizer/g/DirectoryTree/LdapRecord/master.svg?style=flat-square"/>
@@ -45,7 +45,7 @@
 
 ⏲ **Up and Running Fast**
 
-Connect to your LDAP servers and start running queries at lightning speed.
+Connect to your LDAP servers and start running queries in a matter of minutes.
 
 💡 **Fluent Filter Builder**
 

+ 3 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Events/Event.php

@@ -30,9 +30,9 @@ abstract class Event
     /**
      * Constructor.
      *
-     * @param LdapInterface $connection
-     * @param string        $username
-     * @param string        $password
+     * @param  LdapInterface  $connection
+     * @param  string  $username
+     * @param  string  $password
      */
     public function __construct(LdapInterface $connection, $username, $password)
     {

+ 18 - 25
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Auth/Guard.php

@@ -38,8 +38,8 @@ class Guard
     /**
      * Constructor.
      *
-     * @param LdapInterface       $connection
-     * @param DomainConfiguration $configuration
+     * @param  LdapInterface  $connection
+     * @param  DomainConfiguration  $configuration
      */
     public function __construct(LdapInterface $connection, DomainConfiguration $configuration)
     {
@@ -50,10 +50,9 @@ class Guard
     /**
      * Attempt binding a user to the LDAP server.
      *
-     * @param string $username
-     * @param string $password
-     * @param bool   $stayBound
-     *
+     * @param  string  $username
+     * @param  string  $password
+     * @param  bool  $stayBound
      * @return bool
      *
      * @throws UsernameRequiredException
@@ -90,8 +89,8 @@ class Guard
     /**
      * Attempt binding a user to the LDAP server. Supports anonymous binding.
      *
-     * @param string|null $username
-     * @param string|null $password
+     * @param  string|null  $username
+     * @param  string|null  $password
      *
      * @throws BindException
      * @throws \LdapRecord\ConnectionException
@@ -148,8 +147,7 @@ class Guard
     /**
      * Set the event dispatcher instance.
      *
-     * @param DispatcherInterface $dispatcher
-     *
+     * @param  DispatcherInterface  $dispatcher
      * @return void
      */
     public function setDispatcher(DispatcherInterface $dispatcher)
@@ -160,9 +158,8 @@ class Guard
     /**
      * Fire the attempting event.
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return void
      */
     protected function fireAttemptingEvent($username, $password)
@@ -175,9 +172,8 @@ class Guard
     /**
      * Fire the passed event.
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return void
      */
     protected function firePassedEvent($username, $password)
@@ -190,9 +186,8 @@ class Guard
     /**
      * Fire the failed event.
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return void
      */
     protected function fireFailedEvent($username, $password)
@@ -205,9 +200,8 @@ class Guard
     /**
      * Fire the binding event.
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return void
      */
     protected function fireBindingEvent($username, $password)
@@ -220,9 +214,8 @@ class Guard
     /**
      * Fire the bound event.
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return void
      */
     protected function fireBoundEvent($username, $password)

+ 9 - 13
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/DomainConfiguration.php

@@ -58,7 +58,7 @@ class DomainConfiguration
     /**
      * Constructor.
      *
-     * @param array $options
+     * @param  array  $options
      *
      * @throws ConfigurationException When an option value given is an invalid type.
      */
@@ -74,9 +74,8 @@ class DomainConfiguration
     /**
      * Extend the configuration with a custom option, or override an existing.
      *
-     * @param string $option
-     * @param mixed  $default
-     *
+     * @param  string  $option
+     * @param  mixed  $default
      * @return void
      */
     public static function extend($option, $default = null)
@@ -107,8 +106,8 @@ class DomainConfiguration
     /**
      * Set a configuration option.
      *
-     * @param string $key
-     * @param mixed  $value
+     * @param  string  $key
+     * @param  mixed  $value
      *
      * @throws ConfigurationException When an option value given is an invalid type.
      */
@@ -122,8 +121,7 @@ class DomainConfiguration
     /**
      * Returns the value for the specified configuration options.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return mixed
      *
      * @throws ConfigurationException When the option specified does not exist.
@@ -140,8 +138,7 @@ class DomainConfiguration
     /**
      * Checks if a configuration option exists.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     public function has($key)
@@ -152,9 +149,8 @@ class DomainConfiguration
     /**
      * Validate the configuration option.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return bool
      *
      * @throws ConfigurationException When an option value given is an invalid type.

+ 2 - 2
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Configuration/Validators/Validator.php

@@ -30,8 +30,8 @@ abstract class Validator
     /**
      * Constructor.
      *
-     * @param string $key
-     * @param mixed  $value
+     * @param  string  $key
+     * @param  mixed  $value
      */
     public function __construct($key, $value)
     {

+ 49 - 30
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php

@@ -88,8 +88,8 @@ class Connection
     /**
      * Constructor.
      *
-     * @param array              $config
-     * @param LdapInterface|null $ldap
+     * @param  array|DomainConfiguration  $config
+     * @param  LdapInterface|null  $ldap
      */
     public function __construct($config = [], LdapInterface $ldap = null)
     {
@@ -109,15 +109,18 @@ class Connection
     /**
      * Set the connection configuration.
      *
-     * @param array $config
-     *
+     * @param  array|DomainConfiguration  $config
      * @return $this
      *
      * @throws Configuration\ConfigurationException
      */
     public function setConfiguration($config = [])
     {
-        $this->configuration = new DomainConfiguration($config);
+        if (! $config instanceof DomainConfiguration) {
+            $config = new DomainConfiguration($config);
+        }
+
+        $this->configuration = $config;
 
         $this->hosts = $this->configuration->get('hosts');
 
@@ -129,8 +132,7 @@ class Connection
     /**
      * Set the LDAP connection.
      *
-     * @param LdapInterface $ldap
-     *
+     * @param  LdapInterface  $ldap
      * @return $this
      */
     public function setLdapConnection(LdapInterface $ldap)
@@ -143,8 +145,7 @@ class Connection
     /**
      * Set the event dispatcher.
      *
-     * @param DispatcherInterface $dispatcher
-     *
+     * @param  DispatcherInterface  $dispatcher
      * @return $this
      */
     public function setDispatcher(DispatcherInterface $dispatcher)
@@ -192,8 +193,7 @@ class Connection
     /**
      * Set the cache store.
      *
-     * @param CacheInterface $store
-     *
+     * @param  CacheInterface  $store
      * @return $this
      */
     public function setCache(CacheInterface $store)
@@ -238,9 +238,8 @@ class Connection
      *
      * If no username or password is specified, then the configured credentials are used.
      *
-     * @param string|null $username
-     * @param string|null $password
-     *
+     * @param  string|null  $username
+     * @param  string|null  $password
      * @return Connection
      *
      * @throws Auth\BindException
@@ -298,6 +297,16 @@ class Connection
         $this->initialize();
     }
 
+    /**
+     * Clone the connection.
+     *
+     * @return static
+     */
+    public function replicate()
+    {
+        return new static($this->configuration, new $this->ldap);
+    }
+
     /**
      * Disconnect from the LDAP server.
      *
@@ -311,8 +320,7 @@ class Connection
     /**
      * Dispatch an event.
      *
-     * @param object $event
-     *
+     * @param  object  $event
      * @return void
      */
     public function dispatch($event)
@@ -335,8 +343,7 @@ class Connection
     /**
      * Perform the operation on the LDAP connection.
      *
-     * @param Closure $operation
-     *
+     * @param  Closure  $operation
      * @return mixed
      */
     public function run(Closure $operation)
@@ -360,10 +367,26 @@ class Connection
     }
 
     /**
-     * Attempt to get an exception for the cause of failure.
+     * Perform the operation on an isolated LDAP connection.
      *
-     * @param LdapRecordException $e
+     * @param  Closure  $operation
+     * @return mixed
+     */
+    public function isolate(Closure $operation)
+    {
+        $connection = $this->replicate();
+
+        try {
+            return $operation($connection);
+        } finally {
+            $connection->disconnect();
+        }
+    }
+
+    /**
+     * Attempt to get an exception for the cause of failure.
      *
+     * @param  LdapRecordException  $e
      * @return mixed
      */
     protected function getExceptionForCauseOfFailure(LdapRecordException $e)
@@ -383,8 +406,7 @@ class Connection
     /**
      * Run the operation callback on the current LDAP connection.
      *
-     * @param Closure $operation
-     *
+     * @param  Closure  $operation
      * @return mixed
      *
      * @throws LdapRecordException
@@ -439,9 +461,8 @@ class Connection
     /**
      * Attempt to retry an LDAP operation if due to a lost connection.
      *
-     * @param LdapRecordException $e
-     * @param Closure             $operation
-     *
+     * @param  LdapRecordException  $e
+     * @param  Closure  $operation
      * @return mixed
      *
      * @throws LdapRecordException
@@ -461,8 +482,7 @@ class Connection
     /**
      * Retry the operation on the current host.
      *
-     * @param Closure $operation
-     *
+     * @param  Closure  $operation
      * @return mixed
      *
      * @throws LdapRecordException
@@ -483,9 +503,8 @@ class Connection
     /**
      * Attempt the operation again on the next host.
      *
-     * @param LdapRecordException $e
-     * @param Closure             $operation
-     *
+     * @param  LdapRecordException  $e
+     * @param  Closure  $operation
      * @return mixed
      *
      * @throws LdapRecordException

+ 10 - 18
data/web/inc/lib/vendor/directorytree/ldaprecord/src/ConnectionManager.php

@@ -81,9 +81,8 @@ class ConnectionManager
     /**
      * Forward missing method calls onto the instance.
      *
-     * @param string $method
-     * @param mixed  $args
-     *
+     * @param  string  $method
+     * @param  mixed  $args
      * @return mixed
      */
     public function __call($method, $args)
@@ -104,9 +103,8 @@ class ConnectionManager
     /**
      * Add a new connection.
      *
-     * @param Connection  $connection
-     * @param string|null $name
-     *
+     * @param  Connection  $connection
+     * @param  string|null  $name
      * @return $this
      */
     public function add(Connection $connection, $name = null)
@@ -123,8 +121,7 @@ class ConnectionManager
     /**
      * Remove a connection.
      *
-     * @param $name
-     *
+     * @param  $name
      * @return $this
      */
     public function remove($name)
@@ -147,8 +144,7 @@ class ConnectionManager
     /**
      * Get a connection by name or return the default.
      *
-     * @param string|null $name
-     *
+     * @param  string|null  $name
      * @return Connection
      *
      * @throws ContainerException If the given connection does not exist.
@@ -185,8 +181,7 @@ class ConnectionManager
     /**
      * Checks if the connection exists.
      *
-     * @param string $name
-     *
+     * @param  string  $name
      * @return bool
      */
     public function exists($name)
@@ -197,8 +192,7 @@ class ConnectionManager
     /**
      * Set the default connection name.
      *
-     * @param string $name
-     *
+     * @param  string  $name
      * @return $this
      */
     public function setDefault($name = null)
@@ -237,8 +231,7 @@ class ConnectionManager
     /**
      * Set the event logger to use.
      *
-     * @param LoggerInterface $logger
-     *
+     * @param  LoggerInterface  $logger
      * @return void
      */
     public function setLogger(LoggerInterface $logger)
@@ -299,8 +292,7 @@ class ConnectionManager
     /**
      * Set the event dispatcher.
      *
-     * @param DispatcherInterface $dispatcher
-     *
+     * @param  DispatcherInterface  $dispatcher
      * @return void
      */
     public function setDispatcher(DispatcherInterface $dispatcher)

+ 5 - 8
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Container.php

@@ -48,9 +48,8 @@ class Container
     /**
      * Forward missing static calls onto the current instance.
      *
-     * @param string $method
-     * @param mixed  $args
-     *
+     * @param  string  $method
+     * @param  mixed  $args
      * @return mixed
      */
     public static function __callStatic($method, $args)
@@ -71,8 +70,7 @@ class Container
     /**
      * Set the container instance.
      *
-     * @param Container|null $container
-     *
+     * @param  Container|null  $container
      * @return Container|null
      */
     public static function setInstance(self $container = null)
@@ -103,9 +101,8 @@ class Container
     /**
      * Forward missing method calls onto the connection manager.
      *
-     * @param string $method
-     * @param mixed  $args
-     *
+     * @param  string  $method
+     * @param  mixed  $args
      * @return mixed
      */
     public function __call($method, $args)

+ 3 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetailedError.php

@@ -28,9 +28,9 @@ class DetailedError
     /**
      * Constructor.
      *
-     * @param int    $errorCode
-     * @param string $errorMessage
-     * @param string $diagnosticMessage
+     * @param  int  $errorCode
+     * @param  string  $errorMessage
+     * @param  string  $diagnosticMessage
      */
     public function __construct($errorCode, $errorMessage, $diagnosticMessage)
     {

+ 7 - 12
data/web/inc/lib/vendor/directorytree/ldaprecord/src/DetectsErrors.php

@@ -7,8 +7,7 @@ trait DetectsErrors
     /**
      * Determine if the error was caused by a lost connection.
      *
-     * @param string $error
-     *
+     * @param  string  $error
      * @return bool
      */
     protected function causedByLostConnection($error)
@@ -19,8 +18,7 @@ trait DetectsErrors
     /**
      * Determine if the error was caused by lack of pagination support.
      *
-     * @param string $error
-     *
+     * @param  string  $error
      * @return bool
      */
     protected function causedByPaginationSupport($error)
@@ -31,8 +29,7 @@ trait DetectsErrors
     /**
      * Determine if the error was caused by a size limit warning.
      *
-     * @param $error
-     *
+     * @param  $error
      * @return bool
      */
     protected function causedBySizeLimit($error)
@@ -43,8 +40,7 @@ trait DetectsErrors
     /**
      * Determine if the error was caused by a "No such object" warning.
      *
-     * @param string $error
-     *
+     * @param  string  $error
      * @return bool
      */
     protected function causedByNoSuchObject($error)
@@ -55,15 +51,14 @@ trait DetectsErrors
     /**
      * Determine if the error contains the any of the messages.
      *
-     * @param string       $error
-     * @param string|array $messages
-     *
+     * @param  string  $error
+     * @param  string|array  $messages
      * @return bool
      */
     protected function errorContainsMessage($error, $messages = [])
     {
         foreach ((array) $messages as $message) {
-            if (strpos($error, $message) !== false) {
+            if (str_contains((string) $error, $message)) {
                 return true;
             }
         }

+ 3 - 4
data/web/inc/lib/vendor/directorytree/ldaprecord/src/EscapesValues.php

@@ -9,10 +9,9 @@ trait EscapesValues
     /**
      * Prepare a value to be escaped.
      *
-     * @param string $value
-     * @param string $ignore
-     * @param int    $flags
-     *
+     * @param  string  $value
+     * @param  string  $ignore
+     * @param  int  $flags
      * @return EscapedValue
      */
     public function escape($value, $ignore = '', $flags = 0)

+ 1 - 1
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/ConnectionEvent.php

@@ -16,7 +16,7 @@ abstract class ConnectionEvent
     /**
      * Constructor.
      *
-     * @param Connection $connection
+     * @param  Connection  $connection
      */
     public function __construct(Connection $connection)
     {

+ 18 - 27
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Dispatcher.php

@@ -46,7 +46,7 @@ class Dispatcher implements DispatcherInterface
     public function listen($events, $listener)
     {
         foreach ((array) $events as $event) {
-            if (strpos($event, '*') !== false) {
+            if (str_contains((string) $event, '*')) {
                 $this->setupWildcardListen($event, $listener);
             } else {
                 $this->listeners[$event][] = $this->makeListener($listener);
@@ -57,9 +57,8 @@ class Dispatcher implements DispatcherInterface
     /**
      * Setup a wildcard listener callback.
      *
-     * @param string $event
-     * @param mixed  $listener
-     *
+     * @param  string  $event
+     * @param  mixed  $listener
      * @return void
      */
     protected function setupWildcardListen($event, $listener)
@@ -134,9 +133,8 @@ class Dispatcher implements DispatcherInterface
     /**
      * Parse the given event and payload and prepare them for dispatching.
      *
-     * @param mixed $event
-     * @param mixed $payload
-     *
+     * @param  mixed  $event
+     * @param  mixed  $payload
      * @return array
      */
     protected function parseEventAndPayload($event, $payload)
@@ -168,8 +166,7 @@ class Dispatcher implements DispatcherInterface
     /**
      * Get the wildcard listeners for the event.
      *
-     * @param string $eventName
-     *
+     * @param  string  $eventName
      * @return array
      */
     protected function getWildcardListeners($eventName)
@@ -190,9 +187,8 @@ class Dispatcher implements DispatcherInterface
      *
      * This function is a direct excerpt from Laravel's Str::is().
      *
-     * @param string $wildcard
-     * @param string $eventName
-     *
+     * @param  string  $wildcard
+     * @param  string  $eventName
      * @return bool
      */
     protected function wildcardContainsEvent($wildcard, $eventName)
@@ -229,9 +225,8 @@ class Dispatcher implements DispatcherInterface
     /**
      * Add the listeners for the event's interfaces to the given array.
      *
-     * @param string $eventName
-     * @param array  $listeners
-     *
+     * @param  string  $eventName
+     * @param  array  $listeners
      * @return array
      */
     protected function addInterfaceListeners($eventName, array $listeners = [])
@@ -250,9 +245,8 @@ class Dispatcher implements DispatcherInterface
     /**
      * Register an event listener with the dispatcher.
      *
-     * @param \Closure|string $listener
-     * @param bool            $wildcard
-     *
+     * @param  \Closure|string  $listener
+     * @param  bool  $wildcard
      * @return \Closure
      */
     public function makeListener($listener, $wildcard = false)
@@ -273,9 +267,8 @@ class Dispatcher implements DispatcherInterface
     /**
      * Create a class based listener.
      *
-     * @param string $listener
-     * @param bool   $wildcard
-     *
+     * @param  string  $listener
+     * @param  bool  $wildcard
      * @return \Closure
      */
     protected function createClassListener($listener, $wildcard = false)
@@ -295,8 +288,7 @@ class Dispatcher implements DispatcherInterface
     /**
      * Create the class based event callable.
      *
-     * @param string $listener
-     *
+     * @param  string  $listener
      * @return callable
      */
     protected function createClassCallable($listener)
@@ -309,13 +301,12 @@ class Dispatcher implements DispatcherInterface
     /**
      * Parse the class listener into class and method.
      *
-     * @param string $listener
-     *
+     * @param  string  $listener
      * @return array
      */
     protected function parseListenerCallback($listener)
     {
-        return strpos($listener, '@') !== false
+        return str_contains((string) $listener, '@')
             ? explode('@', $listener, 2)
             : [$listener, 'handle'];
     }
@@ -325,7 +316,7 @@ class Dispatcher implements DispatcherInterface
      */
     public function forget($event)
     {
-        if (strpos($event, '*') !== false) {
+        if (str_contains((string) $event, '*')) {
             unset($this->wildcards[$event]);
         } else {
             unset($this->listeners[$event]);

+ 13 - 20
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/DispatcherInterface.php

@@ -7,9 +7,8 @@ interface DispatcherInterface
     /**
      * Register an event listener with the dispatcher.
      *
-     * @param string|array $events
-     * @param mixed        $listener
-     *
+     * @param  string|array  $events
+     * @param  mixed  $listener
      * @return void
      */
     public function listen($events, $listener);
@@ -17,8 +16,7 @@ interface DispatcherInterface
     /**
      * Determine if a given event has listeners.
      *
-     * @param string $eventName
-     *
+     * @param  string  $eventName
      * @return bool
      */
     public function hasListeners($eventName);
@@ -26,9 +24,8 @@ interface DispatcherInterface
     /**
      * Fire an event until the first non-null response is returned.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
      * @return array|null
      */
     public function until($event, $payload = []);
@@ -36,10 +33,9 @@ interface DispatcherInterface
     /**
      * Fire an event and call the listeners.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     * @param bool          $halt
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
      * @return mixed
      */
     public function fire($event, $payload = [], $halt = false);
@@ -47,10 +43,9 @@ interface DispatcherInterface
     /**
      * Fire an event and call the listeners.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     * @param bool          $halt
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
      * @return array|null
      */
     public function dispatch($event, $payload = [], $halt = false);
@@ -58,8 +53,7 @@ interface DispatcherInterface
     /**
      * Get all of the listeners for a given event name.
      *
-     * @param string $eventName
-     *
+     * @param  string  $eventName
      * @return array
      */
     public function getListeners($eventName);
@@ -67,8 +61,7 @@ interface DispatcherInterface
     /**
      * Remove a set of listeners from the dispatcher.
      *
-     * @param string $event
-     *
+     * @param  string  $event
      * @return void
      */
     public function forget($event);

+ 6 - 11
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/Logger.php

@@ -21,7 +21,7 @@ class Logger
     /**
      * Constructor.
      *
-     * @param LoggerInterface|null $logger
+     * @param  LoggerInterface|null  $logger
      */
     public function __construct(LoggerInterface $logger = null)
     {
@@ -31,8 +31,7 @@ class Logger
     /**
      * Logs the given event.
      *
-     * @param mixed $event
-     *
+     * @param  mixed  $event
      * @return void
      */
     public function log($event)
@@ -53,8 +52,7 @@ class Logger
     /**
      * Logs an authentication event.
      *
-     * @param AuthEvent $event
-     *
+     * @param  AuthEvent  $event
      * @return void
      */
     public function auth(AuthEvent $event)
@@ -81,8 +79,7 @@ class Logger
     /**
      * Logs a model event.
      *
-     * @param ModelEvent $event
-     *
+     * @param  ModelEvent  $event
      * @return void
      */
     public function model(ModelEvent $event)
@@ -106,8 +103,7 @@ class Logger
     /**
      * Logs a query event.
      *
-     * @param QueryEvent $event
-     *
+     * @param  QueryEvent  $event
      * @return void
      */
     public function query(QueryEvent $event)
@@ -133,8 +129,7 @@ class Logger
     /**
      * Returns the operational name of the given event.
      *
-     * @param mixed $event
-     *
+     * @param  mixed  $event
      * @return string
      */
     protected function getOperationName($event)

+ 14 - 21
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Events/NullDispatcher.php

@@ -14,7 +14,7 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Constructor.
      *
-     * @param DispatcherInterface $dispatcher
+     * @param  DispatcherInterface  $dispatcher
      */
     public function __construct(DispatcherInterface $dispatcher)
     {
@@ -24,9 +24,8 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Register an event listener with the dispatcher.
      *
-     * @param string|array $events
-     * @param mixed        $listener
-     *
+     * @param  string|array  $events
+     * @param  mixed  $listener
      * @return void
      */
     public function listen($events, $listener)
@@ -37,8 +36,7 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Determine if a given event has listeners.
      *
-     * @param string $eventName
-     *
+     * @param  string  $eventName
      * @return bool
      */
     public function hasListeners($eventName)
@@ -49,9 +47,8 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Fire an event until the first non-null response is returned.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
      * @return null
      */
     public function until($event, $payload = [])
@@ -62,10 +59,9 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Fire an event and call the listeners.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     * @param bool          $halt
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
      * @return null
      */
     public function fire($event, $payload = [], $halt = false)
@@ -76,10 +72,9 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Fire an event and call the listeners.
      *
-     * @param string|object $event
-     * @param mixed         $payload
-     * @param bool          $halt
-     *
+     * @param  string|object  $event
+     * @param  mixed  $payload
+     * @param  bool  $halt
      * @return null
      */
     public function dispatch($event, $payload = [], $halt = false)
@@ -90,8 +85,7 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Get all of the listeners for a given event name.
      *
-     * @param string $eventName
-     *
+     * @param  string  $eventName
      * @return array
      */
     public function getListeners($eventName)
@@ -102,8 +96,7 @@ class NullDispatcher implements DispatcherInterface
     /**
      * Remove a set of listeners from the dispatcher.
      *
-     * @param string $event
-     *
+     * @param  string  $event
      * @return void
      */
     public function forget($event)

+ 7 - 12
data/web/inc/lib/vendor/directorytree/ldaprecord/src/HandlesConnection.php

@@ -148,8 +148,7 @@ trait HandlesConnection
     /**
      * Convert warnings to exceptions for the given operation.
      *
-     * @param Closure $operation
-     *
+     * @param  Closure  $operation
      * @return mixed
      *
      * @throws LdapRecordException
@@ -190,8 +189,7 @@ trait HandlesConnection
     /**
      * Determine if the failed operation should be bypassed.
      *
-     * @param string $method
-     *
+     * @param  string  $method
      * @return bool
      */
     protected function shouldBypassFailure($method)
@@ -202,8 +200,7 @@ trait HandlesConnection
     /**
      * Determine if the error should be bypassed.
      *
-     * @param string $error
-     *
+     * @param  string  $error
      * @return bool
      */
     protected function shouldBypassError($error)
@@ -226,9 +223,8 @@ trait HandlesConnection
     /**
      * Generates an LDAP connection string for each host given.
      *
-     * @param string|array $hosts
-     * @param string       $port
-     *
+     * @param  string|array  $hosts
+     * @param  string  $port
      * @return string
      */
     protected function makeConnectionUris($hosts, $port)
@@ -249,9 +245,8 @@ trait HandlesConnection
     /**
      * Assemble the host URI strings.
      *
-     * @param array|string $hosts
-     * @param string       $port
-     *
+     * @param  array|string  $hosts
+     * @param  string  $port
      * @return array
      */
     protected function assembleHostUris($hosts, $port)

+ 12 - 21
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Ldap.php

@@ -4,7 +4,6 @@ namespace LdapRecord;
 
 use LDAP\Connection as RawLdapConnection;
 
-/** @psalm-suppress UndefinedClass */
 class Ldap implements LdapInterface
 {
     use HandlesConnection, DetectsErrors;
@@ -24,8 +23,7 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-first-entry.php
      *
-     * @param resource $searchResults
-     *
+     * @param  resource  $searchResults
      * @return resource
      */
     public function getFirstEntry($searchResults)
@@ -40,8 +38,7 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-next-entry.php
      *
-     * @param resource $entry
-     *
+     * @param  resource  $entry
      * @return resource
      */
     public function getNextEntry($entry)
@@ -56,8 +53,7 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-get-attributes.php
      *
-     * @param resource $entry
-     *
+     * @param  resource  $entry
      * @return array|false
      */
     public function getAttributes($entry)
@@ -72,8 +68,7 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-count-entries.php
      *
-     * @param resource $searchResults
-     *
+     * @param  resource  $searchResults
      * @return int
      */
     public function countEntries($searchResults)
@@ -88,10 +83,9 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-compare.php
      *
-     * @param string $dn
-     * @param string $attribute
-     * @param string $value
-     *
+     * @param  string  $dn
+     * @param  string  $attribute
+     * @param  string  $value
      * @return mixed
      */
     public function compare($dn, $attribute, $value)
@@ -132,9 +126,8 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-get-values-len.php
      *
-     * @param $entry
-     * @param $attribute
-     *
+     * @param  $entry
+     * @param  $attribute
      * @return array
      */
     public function getValuesLen($entry, $attribute)
@@ -167,8 +160,7 @@ class Ldap implements LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-set-rebind-proc.php
      *
-     * @param callable $callback
-     *
+     * @param  callable  $callback
      * @return bool
      */
     public function setRebindCallback(callable $callback)
@@ -304,7 +296,7 @@ class Ldap implements LdapInterface
     public function bind($username, $password)
     {
         return $this->bound = $this->executeFailableOperation(function () use ($username, $password) {
-            return ldap_bind($this->connection, $username, html_entity_decode($password));
+            return ldap_bind($this->connection, $username, $password ? html_entity_decode($password) : null);
         });
     }
 
@@ -462,8 +454,7 @@ class Ldap implements LdapInterface
     /**
      * Extract the diagnostic code from the message.
      *
-     * @param string $message
-     *
+     * @param  string  $message
      * @return string|bool
      */
     public function extractDiagnosticCode($message)

+ 89 - 113
data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapInterface.php

@@ -9,28 +9,28 @@ interface LdapInterface
      *
      * @var string
      */
-    const PROTOCOL_SSL = 'ldaps://';
+    public const PROTOCOL_SSL = 'ldaps://';
 
     /**
      * The standard LDAP protocol string.
      *
      * @var string
      */
-    const PROTOCOL = 'ldap://';
+    public const PROTOCOL = 'ldap://';
 
     /**
      * The LDAP SSL port number.
      *
      * @var string
      */
-    const PORT_SSL = 636;
+    public const PORT_SSL = 636;
 
     /**
      * The standard LDAP port number.
      *
      * @var string
      */
-    const PORT = 389;
+    public const PORT = 389;
 
     /**
      * Various useful server control OID's.
@@ -38,37 +38,36 @@ interface LdapInterface
      * @see https://ldap.com/ldap-oid-reference-guide/
      * @see http://msdn.microsoft.com/en-us/library/cc223359.aspx
      */
-    const OID_SERVER_START_TLS = '1.3.6.1.4.1.1466.20037';
-    const OID_SERVER_PAGED_RESULTS = '1.2.840.113556.1.4.319';
-    const OID_SERVER_SHOW_DELETED = '1.2.840.113556.1.4.417';
-    const OID_SERVER_SORT = '1.2.840.113556.1.4.473';
-    const OID_SERVER_CROSSDOM_MOVE_TARGET = '1.2.840.113556.1.4.521';
-    const OID_SERVER_NOTIFICATION = '1.2.840.113556.1.4.528';
-    const OID_SERVER_EXTENDED_DN = '1.2.840.113556.1.4.529';
-    const OID_SERVER_LAZY_COMMIT = '1.2.840.113556.1.4.619';
-    const OID_SERVER_SD_FLAGS = '1.2.840.113556.1.4.801';
-    const OID_SERVER_TREE_DELETE = '1.2.840.113556.1.4.805';
-    const OID_SERVER_DIRSYNC = '1.2.840.113556.1.4.841';
-    const OID_SERVER_VERIFY_NAME = '1.2.840.113556.1.4.1338';
-    const OID_SERVER_DOMAIN_SCOPE = '1.2.840.113556.1.4.1339';
-    const OID_SERVER_SEARCH_OPTIONS = '1.2.840.113556.1.4.1340';
-    const OID_SERVER_PERMISSIVE_MODIFY = '1.2.840.113556.1.4.1413';
-    const OID_SERVER_ASQ = '1.2.840.113556.1.4.1504';
-    const OID_SERVER_FAST_BIND = '1.2.840.113556.1.4.1781';
-    const OID_SERVER_CONTROL_VLVREQUEST = '2.16.840.1.113730.3.4.9';
+    public const OID_SERVER_START_TLS = '1.3.6.1.4.1.1466.20037';
+    public const OID_SERVER_PAGED_RESULTS = '1.2.840.113556.1.4.319';
+    public const OID_SERVER_SHOW_DELETED = '1.2.840.113556.1.4.417';
+    public const OID_SERVER_SORT = '1.2.840.113556.1.4.473';
+    public const OID_SERVER_CROSSDOM_MOVE_TARGET = '1.2.840.113556.1.4.521';
+    public const OID_SERVER_NOTIFICATION = '1.2.840.113556.1.4.528';
+    public const OID_SERVER_EXTENDED_DN = '1.2.840.113556.1.4.529';
+    public const OID_SERVER_LAZY_COMMIT = '1.2.840.113556.1.4.619';
+    public const OID_SERVER_SD_FLAGS = '1.2.840.113556.1.4.801';
+    public const OID_SERVER_TREE_DELETE = '1.2.840.113556.1.4.805';
+    public const OID_SERVER_DIRSYNC = '1.2.840.113556.1.4.841';
+    public const OID_SERVER_VERIFY_NAME = '1.2.840.113556.1.4.1338';
+    public const OID_SERVER_DOMAIN_SCOPE = '1.2.840.113556.1.4.1339';
+    public const OID_SERVER_SEARCH_OPTIONS = '1.2.840.113556.1.4.1340';
+    public const OID_SERVER_PERMISSIVE_MODIFY = '1.2.840.113556.1.4.1413';
+    public const OID_SERVER_ASQ = '1.2.840.113556.1.4.1504';
+    public const OID_SERVER_FAST_BIND = '1.2.840.113556.1.4.1781';
+    public const OID_SERVER_CONTROL_VLVREQUEST = '2.16.840.1.113730.3.4.9';
 
     /**
      * Query OID's.
      *
      * @see https://ldapwiki.com/wiki/LDAP_MATCHING_RULE_IN_CHAIN
      */
-    const OID_MATCHING_RULE_IN_CHAIN = '1.2.840.113556.1.4.1941';
+    public const OID_MATCHING_RULE_IN_CHAIN = '1.2.840.113556.1.4.1941';
 
     /**
      * Set the current connection to use SSL.
      *
-     * @param bool $enabled
-     *
+     * @param  bool  $enabled
      * @return $this
      */
     public function ssl();
@@ -83,8 +82,7 @@ interface LdapInterface
     /**
      * Set the current connection to use TLS.
      *
-     * @param bool $enabled
-     *
+     * @param  bool  $enabled
      * @return $this
      */
     public function tls();
@@ -138,8 +136,7 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-get-entries.php
      *
-     * @param resource $searchResults
-     *
+     * @param  resource  $searchResults
      * @return array
      */
     public function getEntries($searchResults);
@@ -169,9 +166,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-set-option.php
      *
-     * @param int   $option
-     * @param mixed $value
-     *
+     * @param  int  $option
+     * @param  mixed  $value
      * @return bool
      */
     public function setOption($option, $value);
@@ -179,8 +175,7 @@ interface LdapInterface
     /**
      * Set options on the current connection.
      *
-     * @param array $options
-     *
+     * @param  array  $options
      * @return void
      */
     public function setOptions(array $options = []);
@@ -190,9 +185,8 @@ interface LdapInterface
      *
      * @see https://www.php.net/manual/en/function.ldap-get-option.php
      *
-     * @param int   $option
-     * @param mixed $value
-     *
+     * @param  int  $option
+     * @param  mixed  $value
      * @return mixed
      */
     public function getOption($option, &$value = null);
@@ -213,9 +207,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-start-tls.php
      *
-     * @param string|array $hosts
-     * @param int          $port
-     *
+     * @param  string|array  $hosts
+     * @param  int  $port
      * @return resource|false
      */
     public function connect($hosts = [], $port = 389);
@@ -236,15 +229,14 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-search.php
      *
-     * @param string $dn
-     * @param string $filter
-     * @param array  $fields
-     * @param bool   $onlyAttributes
-     * @param int    $size
-     * @param int    $time
-     * @param int    $deref
-     * @param array  $serverControls
-     *
+     * @param  string  $dn
+     * @param  string  $filter
+     * @param  array  $fields
+     * @param  bool  $onlyAttributes
+     * @param  int  $size
+     * @param  int  $time
+     * @param  int  $deref
+     * @param  array  $serverControls
      * @return resource
      */
     public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []);
@@ -254,15 +246,14 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-list.php
      *
-     * @param string $dn
-     * @param string $filter
-     * @param array  $fields
-     * @param bool   $onlyAttributes
-     * @param int    $size
-     * @param int    $time
-     * @param int    $deref
-     * @param array  $serverControls
-     *
+     * @param  string  $dn
+     * @param  string  $filter
+     * @param  array  $fields
+     * @param  bool  $onlyAttributes
+     * @param  int  $size
+     * @param  int  $time
+     * @param  int  $deref
+     * @param  array  $serverControls
      * @return resource
      */
     public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []);
@@ -272,15 +263,14 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-read.php
      *
-     * @param string $dn
-     * @param string $filter
-     * @param array  $fields
-     * @param bool   $onlyAttributes
-     * @param int    $size
-     * @param int    $time
-     * @param int    $deref
-     * @param array  $serverControls
-     *
+     * @param  string  $dn
+     * @param  string  $filter
+     * @param  array  $fields
+     * @param  bool  $onlyAttributes
+     * @param  int  $size
+     * @param  int  $time
+     * @param  int  $deref
+     * @param  array  $serverControls
      * @return resource
      */
     public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = []);
@@ -290,13 +280,12 @@ interface LdapInterface
      *
      * @see https://www.php.net/manual/en/function.ldap-parse-result.php
      *
-     * @param resource $result
-     * @param int      $errorCode
-     * @param ?string  $dn
-     * @param ?string  $errorMessage
-     * @param ?array   $referrals
-     * @param ?array   $serverControls
-     *
+     * @param  resource  $result
+     * @param  int  $errorCode
+     * @param  ?string  $dn
+     * @param  ?string  $errorMessage
+     * @param  ?array  $referrals
+     * @param  ?array  $serverControls
      * @return bool
      */
     public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = []);
@@ -307,9 +296,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-bind.php
      *
-     * @param string $username
-     * @param string $password
-     *
+     * @param  string  $username
+     * @param  string  $password
      * @return bool
      *
      * @throws LdapRecordException
@@ -321,9 +309,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-add.php
      *
-     * @param string $dn
-     * @param array  $entry
-     *
+     * @param  string  $dn
+     * @param  array  $entry
      * @return bool
      *
      * @throws LdapRecordException
@@ -335,8 +322,7 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-delete.php
      *
-     * @param string $dn
-     *
+     * @param  string  $dn
      * @return bool
      *
      * @throws LdapRecordException
@@ -348,11 +334,10 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-rename.php
      *
-     * @param string $dn
-     * @param string $newRdn
-     * @param string $newParent
-     * @param bool   $deleteOldRdn
-     *
+     * @param  string  $dn
+     * @param  string  $newRdn
+     * @param  string  $newParent
+     * @param  bool  $deleteOldRdn
      * @return bool
      *
      * @throws LdapRecordException
@@ -364,9 +349,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-modify.php
      *
-     * @param string $dn
-     * @param array  $entry
-     *
+     * @param  string  $dn
+     * @param  array  $entry
      * @return bool
      *
      * @throws LdapRecordException
@@ -378,9 +362,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-modify-batch.php
      *
-     * @param string $dn
-     * @param array  $values
-     *
+     * @param  string  $dn
+     * @param  array  $values
      * @return bool
      *
      * @throws LdapRecordException
@@ -392,9 +375,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-mod-add.php
      *
-     * @param string $dn
-     * @param array  $entry
-     *
+     * @param  string  $dn
+     * @param  array  $entry
      * @return bool
      *
      * @throws LdapRecordException
@@ -406,9 +388,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-mod-replace.php
      *
-     * @param string $dn
-     * @param array  $entry
-     *
+     * @param  string  $dn
+     * @param  array  $entry
      * @return bool
      *
      * @throws LdapRecordException
@@ -420,9 +401,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-mod-del.php
      *
-     * @param string $dn
-     * @param array  $entry
-     *
+     * @param  string  $dn
+     * @param  array  $entry
      * @return bool
      *
      * @throws LdapRecordException
@@ -434,10 +414,9 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-control-paged-result.php
      *
-     * @param int    $pageSize
-     * @param bool   $isCritical
-     * @param string $cookie
-     *
+     * @param  int  $pageSize
+     * @param  bool  $isCritical
+     * @param  string  $cookie
      * @return bool
      */
     public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '');
@@ -447,9 +426,8 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-control-paged-result-response.php
      *
-     * @param resource $result
-     * @param string   $cookie
-     *
+     * @param  resource  $result
+     * @param  string  $cookie
      * @return bool
      */
     public function controlPagedResultResponse($result, &$cookie);
@@ -459,8 +437,7 @@ interface LdapInterface
      *
      * @see https://www.php.net/manual/en/function.ldap-free-result.php
      *
-     * @param resource $result
-     *
+     * @param  resource  $result
      * @return bool
      */
     public function freeResult($result);
@@ -479,8 +456,7 @@ interface LdapInterface
      *
      * @see http://php.net/manual/en/function.ldap-err2str.php
      *
-     * @param int $number
-     *
+     * @param  int  $number
      * @return string
      */
     public function err2Str($number);

+ 3 - 5
data/web/inc/lib/vendor/directorytree/ldaprecord/src/LdapRecordException.php

@@ -16,9 +16,8 @@ class LdapRecordException extends Exception
     /**
      * Create a new Bind Exception with a detailed connection error.
      *
-     * @param Exception          $e
-     * @param DetailedError|null $error
-     *
+     * @param  Exception  $e
+     * @param  DetailedError|null  $error
      * @return $this
      */
     public static function withDetailedError(Exception $e, DetailedError $error = null)
@@ -29,8 +28,7 @@ class LdapRecordException extends Exception
     /**
      * Set the detailed error.
      *
-     * @param DetailedError|null $error
-     *
+     * @param  DetailedError|null  $error
      * @return $this
      */
     public function setDetailedError(DetailedError $error = null)

+ 3 - 4
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Concerns/HasPrimaryGroup.php

@@ -9,10 +9,9 @@ trait HasPrimaryGroup
     /**
      * Returns a new has one primary group relationship.
      *
-     * @param mixed  $related
-     * @param string $relationKey
-     * @param string $foreignKey
-     *
+     * @param  mixed  $related
+     * @param  string  $relationKey
+     * @param  string  $foreignKey
      * @return HasOnePrimaryGroup
      */
     public function hasOnePrimaryGroup($related, $relationKey, $foreignKey = 'primarygroupid')

+ 69 - 33
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Entry.php

@@ -9,6 +9,7 @@ use LdapRecord\Models\Entry as BaseEntry;
 use LdapRecord\Models\Events\Updated;
 use LdapRecord\Models\Types\ActiveDirectory;
 use LdapRecord\Query\Model\ActiveDirectoryBuilder;
+use LdapRecord\Support\Arr;
 
 /** @mixin ActiveDirectoryBuilder */
 class Entry extends BaseEntry implements ActiveDirectory
@@ -50,20 +51,46 @@ class Entry extends BaseEntry implements ActiveDirectory
     /**
      * @inheritdoc
      */
-    public function getConvertedSid()
+    public function getConvertedSid($sid = null)
     {
         try {
-            return (string) new Sid($this->getObjectSid());
+            return (string) $this->newObjectSid(
+                $sid ?? $this->getObjectSid()
+            );
         } catch (InvalidArgumentException $e) {
             return;
         }
     }
 
     /**
-     * Create a new query builder.
+     * @inheritdoc
+     */
+    public function getBinarySid($sid = null)
+    {
+        try {
+            return $this->newObjectSid(
+                $sid ?? $this->getObjectSid()
+            )->getBinary();
+        } catch (InvalidArgumentException $e) {
+            return;
+        }
+    }
+
+    /**
+     * Make a new object Sid instance.
      *
-     * @param Connection $connection
+     * @param  string  $value
+     * @return Sid
+     */
+    protected function newObjectSid($value)
+    {
+        return new Sid($value);
+    }
+
+    /**
+     * Create a new query builder.
      *
+     * @param  Connection  $connection
      * @return ActiveDirectoryBuilder
      */
     public function newQueryBuilder(Connection $connection)
@@ -84,8 +111,7 @@ class Entry extends BaseEntry implements ActiveDirectory
     /**
      * Restore a deleted object.
      *
-     * @param string|null $newParentDn
-     *
+     * @param  string|null  $newParentDn
      * @return bool
      *
      * @throws \LdapRecord\LdapRecordException
@@ -114,24 +140,6 @@ class Entry extends BaseEntry implements ActiveDirectory
         $this->save(['isDeleted' => null]);
     }
 
-    /**
-     * Get the RootDSE (AD schema) record from the directory.
-     *
-     * @param string|null $connection
-     *
-     * @return static
-     *
-     * @throws \LdapRecord\Models\ModelNotFoundException
-     */
-    public static function getRootDse($connection = null)
-    {
-        return static::on($connection ?? (new static())->getConnectionName())
-            ->in(null)
-            ->read()
-            ->whereHas('objectclass')
-            ->firstOrFail();
-    }
-
     /**
      * Get the objects restore location.
      *
@@ -143,22 +151,50 @@ class Entry extends BaseEntry implements ActiveDirectory
     }
 
     /**
-     * Converts attributes for JSON serialization.
-     *
-     * @param array $attributes
+     * Convert the attributes for JSON serialization.
      *
+     * @param  array  $attributes
      * @return array
      */
     protected function convertAttributesForJson(array $attributes = [])
     {
         $attributes = parent::convertAttributesForJson($attributes);
 
-        if ($this->hasAttribute($this->sidKey)) {
-            // If the model has a SID set, we need to convert it due to it being in
-            // binary. Otherwise we will receive a JSON serialization exception.
-            return array_replace($attributes, [
-                $this->sidKey => [$this->getConvertedSid()],
-            ]);
+        // If the model has a SID set, we need to convert it to its
+        // string format, due to it being in binary. Otherwise
+        // we will receive a JSON serialization exception.
+        if (isset($attributes[$this->sidKey])) {
+            $attributes[$this->sidKey] = [$this->getConvertedSid(
+                Arr::first($attributes[$this->sidKey])
+            )];
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Convert the attributes from JSON serialization.
+     *
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function convertAttributesFromJson(array $attributes = [])
+    {
+        $attributes = parent::convertAttributesFromJson($attributes);
+
+        // Here we are converting the model's GUID and SID attributes
+        // back to their original values from serialization, so that
+        // their original value may be used and compared against.
+        if (isset($attributes[$this->guidKey])) {
+            $attributes[$this->guidKey] = [$this->getBinaryGuid(
+                Arr::first($attributes[$this->guidKey])
+            )];
+        }
+
+        if (isset($attributes[$this->sidKey])) {
+            $attributes[$this->sidKey] = [$this->getBinarySid(
+                Arr::first($attributes[$this->sidKey])
+            )];
         }
 
         return $attributes;

+ 2 - 4
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Relations/HasOnePrimaryGroup.php

@@ -10,8 +10,7 @@ class HasOnePrimaryGroup extends HasOne
     /**
      * Get the foreign model by the given value.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return Model|null
      */
     protected function getForeignModelByValue($value)
@@ -26,8 +25,7 @@ class HasOnePrimaryGroup extends HasOne
      *
      * Retrieves the last RID from the models Object SID.
      *
-     * @param Model $model
-     *
+     * @param  Model  $model
      * @return string
      */
     protected function getForeignValueFromModel(Model $model)

+ 2 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/HasServerRoleAttribute.php

@@ -11,9 +11,8 @@ class HasServerRoleAttribute implements Scope
     /**
      * Includes condition of having a serverRole attribute.
      *
-     * @param Builder $query
-     * @param Model   $model
-     *
+     * @param  Builder  $query
+     * @param  Model  $model
      * @return void
      */
     public function apply(Builder $query, Model $model)

+ 3 - 5
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/InConfigurationContext.php

@@ -12,9 +12,8 @@ class InConfigurationContext implements Scope
     /**
      * Refines the base dn to be inside the configuration context.
      *
-     * @param Builder $query
-     * @param Model   $model
-     *
+     * @param  Builder  $query
+     * @param  Model  $model
      * @return void
      *
      * @throws \LdapRecord\Models\ModelNotFoundException
@@ -27,8 +26,7 @@ class InConfigurationContext implements Scope
     /**
      * Get the LDAP server configuration naming context distinguished name.
      *
-     * @param Model $model
-     *
+     * @param  Model  $model
      * @return mixed
      *
      * @throws \LdapRecord\Models\ModelNotFoundException

+ 2 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/Scopes/RejectComputerObjectClass.php

@@ -11,9 +11,8 @@ class RejectComputerObjectClass implements Scope
     /**
      * Prevent computer objects from being included in results.
      *
-     * @param Builder $query
-     * @param Model   $model
-     *
+     * @param  Builder  $query
+     * @param  Model  $model
      * @return void
      */
     public function apply(Builder $query, Model $model)

+ 37 - 7
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/ActiveDirectory/User.php

@@ -6,6 +6,7 @@ use Carbon\Carbon;
 use Illuminate\Contracts\Auth\Authenticatable;
 use LdapRecord\Models\ActiveDirectory\Concerns\HasPrimaryGroup;
 use LdapRecord\Models\ActiveDirectory\Scopes\RejectComputerObjectClass;
+use LdapRecord\Models\Attributes\AccountControl;
 use LdapRecord\Models\Concerns\CanAuthenticate;
 use LdapRecord\Models\Concerns\HasPassword;
 use LdapRecord\Query\Model\Builder;
@@ -71,6 +72,38 @@ class User extends Entry implements Authenticatable
         static::addGlobalScope(new RejectComputerObjectClass());
     }
 
+    /**
+     * Determine if the user's account is enabled.
+     *
+     * @return bool
+     */
+    public function isEnabled()
+    {
+        return ! $this->isDisabled();
+    }
+
+    /**
+     * Determine if the user's account is disabled.
+     *
+     * @return bool
+     */
+    public function isDisabled()
+    {
+        return $this->accountControl()->has(AccountControl::ACCOUNTDISABLE);
+    }
+
+    /**
+     * Get the user's account control.
+     *
+     * @return AccountControl
+     */
+    public function accountControl()
+    {
+        return new AccountControl(
+            $this->getFirstAttribute('userAccountControl')
+        );
+    }
+
     /**
      * The groups relationship.
      *
@@ -110,8 +143,7 @@ class User extends Entry implements Authenticatable
     /**
      * Scopes the query to exchange mailbox users.
      *
-     * @param Builder $query
-     *
+     * @param  Builder  $query
      * @return Builder
      */
     public function scopeWhereHasMailbox(Builder $query)
@@ -122,8 +154,7 @@ class User extends Entry implements Authenticatable
     /**
      * Scopes the query to users having a lockout value set.
      *
-     * @param Builder $query
-     *
+     * @param  Builder  $query
      * @return Builder
      */
     public function scopeWhereHasLockout(Builder $query)
@@ -137,9 +168,8 @@ class User extends Entry implements Authenticatable
      * @see https://ldapwiki.com/wiki/Active%20Directory%20Account%20Lockout
      * @see https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/account-lockout-duration
      *
-     * @param string|int $localTimezone
-     * @param int|null   $durationInMinutes
-     *
+     * @param  string|int  $localTimezone
+     * @param  int|null  $durationInMinutes
      * @return bool
      */
     public function isLockedOut($localTimezone, $durationInMinutes = null)

+ 30 - 37
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/AccountControl.php

@@ -6,49 +6,49 @@ use ReflectionClass;
 
 class AccountControl
 {
-    const SCRIPT = 1;
+    public const SCRIPT = 1;
 
-    const ACCOUNTDISABLE = 2;
+    public const ACCOUNTDISABLE = 2;
 
-    const HOMEDIR_REQUIRED = 8;
+    public const HOMEDIR_REQUIRED = 8;
 
-    const LOCKOUT = 16;
+    public const LOCKOUT = 16;
 
-    const PASSWD_NOTREQD = 32;
+    public const PASSWD_NOTREQD = 32;
 
-    const PASSWD_CANT_CHANGE = 64;
+    public const PASSWD_CANT_CHANGE = 64;
 
-    const ENCRYPTED_TEXT_PWD_ALLOWED = 128;
+    public const ENCRYPTED_TEXT_PWD_ALLOWED = 128;
 
-    const TEMP_DUPLICATE_ACCOUNT = 256;
+    public const TEMP_DUPLICATE_ACCOUNT = 256;
 
-    const NORMAL_ACCOUNT = 512;
+    public const NORMAL_ACCOUNT = 512;
 
-    const INTERDOMAIN_TRUST_ACCOUNT = 2048;
+    public const INTERDOMAIN_TRUST_ACCOUNT = 2048;
 
-    const WORKSTATION_TRUST_ACCOUNT = 4096;
+    public const WORKSTATION_TRUST_ACCOUNT = 4096;
 
-    const SERVER_TRUST_ACCOUNT = 8192;
+    public const SERVER_TRUST_ACCOUNT = 8192;
 
-    const DONT_EXPIRE_PASSWORD = 65536;
+    public const DONT_EXPIRE_PASSWORD = 65536;
 
-    const MNS_LOGON_ACCOUNT = 131072;
+    public const MNS_LOGON_ACCOUNT = 131072;
 
-    const SMARTCARD_REQUIRED = 262144;
+    public const SMARTCARD_REQUIRED = 262144;
 
-    const TRUSTED_FOR_DELEGATION = 524288;
+    public const TRUSTED_FOR_DELEGATION = 524288;
 
-    const NOT_DELEGATED = 1048576;
+    public const NOT_DELEGATED = 1048576;
 
-    const USE_DES_KEY_ONLY = 2097152;
+    public const USE_DES_KEY_ONLY = 2097152;
 
-    const DONT_REQ_PREAUTH = 4194304;
+    public const DONT_REQ_PREAUTH = 4194304;
 
-    const PASSWORD_EXPIRED = 8388608;
+    public const PASSWORD_EXPIRED = 8388608;
 
-    const TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216;
+    public const TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216;
 
-    const PARTIAL_SECRETS_ACCOUNT = 67108864;
+    public const PARTIAL_SECRETS_ACCOUNT = 67108864;
 
     /**
      * The account control flag values.
@@ -60,7 +60,7 @@ class AccountControl
     /**
      * Constructor.
      *
-     * @param ?int $flag
+     * @param  ?int  $flag
      */
     public function __construct($flag = null)
     {
@@ -92,8 +92,7 @@ class AccountControl
     /**
      * Add the flag to the account control values.
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return $this
      */
     public function add($flag)
@@ -108,8 +107,7 @@ class AccountControl
     /**
      * Remove the flag from the account control.
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return $this
      */
     public function remove($flag)
@@ -122,8 +120,7 @@ class AccountControl
     /**
      * Extract and apply the flag.
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return void
      */
     public function apply($flag)
@@ -134,8 +131,7 @@ class AccountControl
     /**
      * Determine if the account control contains the given UAC flag(s).
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return bool
      */
     public function has($flag)
@@ -154,8 +150,7 @@ class AccountControl
     /**
      * Determine if the account control does not contain the given UAC flag(s).
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return bool
      */
     public function doesntHave($flag)
@@ -441,8 +436,7 @@ class AccountControl
     /**
      * Set the account control values.
      *
-     * @param array<int, int> $flags
-     *
+     * @param  array<int, int>  $flags
      * @return void
      */
     public function setValues(array $flags)
@@ -483,8 +477,7 @@ class AccountControl
     /**
      * Extracts the given flag into an array of flags used.
      *
-     * @param int $flag
-     *
+     * @param  int  $flag
      * @return array
      */
     public function extractFlags($flag)

+ 18 - 34
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedName.php

@@ -19,7 +19,7 @@ class DistinguishedName
     /**
      * Constructor.
      *
-     * @param string|null $value
+     * @param  string|null  $value
      */
     public function __construct($value = null)
     {
@@ -39,8 +39,7 @@ class DistinguishedName
     /**
      * Alias of the "build" method.
      *
-     * @param string|null $value
-     *
+     * @param  string|null  $value
      * @return DistinguishedNameBuilder
      */
     public static function of($value = null)
@@ -51,8 +50,7 @@ class DistinguishedName
     /**
      * Get a new DN builder object from the given DN.
      *
-     * @param string|null $value
-     *
+     * @param  string|null  $value
      * @return DistinguishedNameBuilder
      */
     public static function build($value = null)
@@ -63,8 +61,7 @@ class DistinguishedName
     /**
      * Make a new distinguished name instance.
      *
-     * @param string|null $value
-     *
+     * @param  string|null  $value
      * @return static
      */
     public static function make($value = null)
@@ -75,8 +72,7 @@ class DistinguishedName
     /**
      * Determine if the given value is a valid distinguished name.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return bool
      */
     public static function isValid($value)
@@ -87,8 +83,7 @@ class DistinguishedName
     /**
      * Explode a distinguished name into relative distinguished names.
      *
-     * @param string $dn
-     *
+     * @param  string  $dn
      * @return array
      */
     public static function explode($dn)
@@ -111,8 +106,7 @@ class DistinguishedName
     /**
      * Un-escapes a hexadecimal string into its original string representation.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return string
      */
     public static function unescape($value)
@@ -125,8 +119,7 @@ class DistinguishedName
     /**
      * Explode the RDN into an attribute and value.
      *
-     * @param string $rdn
-     *
+     * @param  string  $rdn
      * @return array
      */
     public static function explodeRdn($rdn)
@@ -137,8 +130,7 @@ class DistinguishedName
     /**
      * Implode the component attribute and value into an RDN.
      *
-     * @param string $rdn
-     *
+     * @param  string  $rdn
      * @return string
      */
     public static function makeRdn(array $component)
@@ -159,8 +151,7 @@ class DistinguishedName
     /**
      * Set the underlying value.
      *
-     * @param string|null $value
-     *
+     * @param  string|null  $value
      * @return $this
      */
     public function set($value)
@@ -337,8 +328,7 @@ class DistinguishedName
     /**
      * Determine if the current distinguished name is a parent of the given child.
      *
-     * @param DistinguishedName $child
-     *
+     * @param  DistinguishedName  $child
      * @return bool
      */
     public function isParentOf(self $child)
@@ -349,8 +339,7 @@ class DistinguishedName
     /**
      * Determine if the current distinguished name is a child of the given parent.
      *
-     * @param DistinguishedName $parent
-     *
+     * @param  DistinguishedName  $parent
      * @return bool
      */
     public function isChildOf(self $parent)
@@ -370,8 +359,7 @@ class DistinguishedName
     /**
      * Determine if the current distinguished name is an ancestor of the descendant.
      *
-     * @param DistinguishedName $descendant
-     *
+     * @param  DistinguishedName  $descendant
      * @return bool
      */
     public function isAncestorOf(self $descendant)
@@ -382,8 +370,7 @@ class DistinguishedName
     /**
      * Determine if the current distinguished name is a descendant of the ancestor.
      *
-     * @param DistinguishedName $ancestor
-     *
+     * @param  DistinguishedName  $ancestor
      * @return bool
      */
     public function isDescendantOf(self $ancestor)
@@ -407,9 +394,8 @@ class DistinguishedName
     /**
      * Compare whether the two distinguished name values are equal.
      *
-     * @param array $values
-     * @param array $other
-     *
+     * @param  array  $values
+     * @param  array  $other
      * @return bool
      */
     protected function compare(array $values, array $other)
@@ -420,8 +406,7 @@ class DistinguishedName
     /**
      * Recase the array values.
      *
-     * @param array $values
-     *
+     * @param  array  $values
      * @return array
      */
     protected function recase(array $values)
@@ -432,8 +417,7 @@ class DistinguishedName
     /**
      * Normalize the string value.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return string
      */
     protected function normalize($value)

+ 18 - 28
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/DistinguishedNameBuilder.php

@@ -26,7 +26,7 @@ class DistinguishedNameBuilder
     /**
      * Constructor.
      *
-     * @param string|null $value
+     * @param  string|null  $value
      */
     public function __construct($dn = null)
     {
@@ -38,9 +38,8 @@ class DistinguishedNameBuilder
     /**
      * Forward missing method calls onto the Distinguished Name object.
      *
-     * @param string $method
-     * @param array  $args
-     *
+     * @param  string  $method
+     * @param  array  $args
      * @return mixed
      */
     public function __call($method, $args)
@@ -61,9 +60,8 @@ class DistinguishedNameBuilder
     /**
      * Prepend an RDN onto the DN.
      *
-     * @param string|array $attribute
-     * @param string|null  $value
-     *
+     * @param  string|array  $attribute
+     * @param  string|null  $value
      * @return $this
      */
     public function prepend($attribute, $value = null)
@@ -79,9 +77,8 @@ class DistinguishedNameBuilder
     /**
      * Append an RDN onto the DN.
      *
-     * @param string|array $attribute
-     * @param string|null  $value
-     *
+     * @param  string|array  $attribute
+     * @param  string|null  $value
      * @return $this
      */
     public function append($attribute, $value = null)
@@ -97,9 +94,8 @@ class DistinguishedNameBuilder
     /**
      * Componentize the attribute and value.
      *
-     * @param string|array $attribute
-     * @param string|null  $value
-     *
+     * @param  string|array  $attribute
+     * @param  string|null  $value
      * @return array
      */
     protected function componentize($attribute, $value = null)
@@ -125,8 +121,7 @@ class DistinguishedNameBuilder
     /**
      * Make a componentized array by exploding the value if it's a string.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return array
      */
     protected function makeComponentizedArray($value)
@@ -137,9 +132,8 @@ class DistinguishedNameBuilder
     /**
      * Make an appendable component array from the attribute and value.
      *
-     * @param string|array $attribute
-     * @param string|null  $value
-     *
+     * @param  string|array  $attribute
+     * @param  string|null  $value
      * @return array
      */
     protected function makeAppendableComponent($attribute, $value = null)
@@ -150,9 +144,8 @@ class DistinguishedNameBuilder
     /**
      * Pop an RDN off of the end of the DN.
      *
-     * @param int   $amount
-     * @param array $removed
-     *
+     * @param  int  $amount
+     * @param  array  $removed
      * @return $this
      */
     public function pop($amount = 1, &$removed = [])
@@ -167,9 +160,8 @@ class DistinguishedNameBuilder
     /**
      * Shift an RDN off of the beginning of the DN.
      *
-     * @param int   $amount
-     * @param array $removed
-     *
+     * @param  int  $amount
+     * @param  array  $removed
      * @return $this
      */
     public function shift($amount = 1, &$removed = [])
@@ -196,8 +188,7 @@ class DistinguishedNameBuilder
     /**
      * Get the components of the DN.
      *
-     * @param null|string $type
-     *
+     * @param  null|string  $type
      * @return array
      */
     public function components($type = null)
@@ -210,8 +201,7 @@ class DistinguishedNameBuilder
     /**
      * Get the components of a particular type.
      *
-     * @param string $type
-     *
+     * @param  string  $type
      * @return array
      */
     protected function componentsOfType($type)

+ 4 - 5
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/EscapedValue.php

@@ -28,9 +28,9 @@ class EscapedValue
     /**
      * Constructor.
      *
-     * @param string $value
-     * @param string $ignore
-     * @param int    $flags
+     * @param  string  $value
+     * @param  string  $ignore
+     * @param  int  $flags
      */
     public function __construct($value, $ignore = '', $flags = 0)
     {
@@ -72,8 +72,7 @@ class EscapedValue
     /**
      * Set the characters to exclude from being escaped.
      *
-     * @param string $characters
-     *
+     * @param  string  $characters
      * @return $this
      */
     public function ignore($characters)

+ 6 - 9
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Guid.php

@@ -50,8 +50,7 @@ class Guid
     /**
      * Determines if the specified GUID is valid.
      *
-     * @param string $guid
-     *
+     * @param  string  $guid
      * @return bool
      */
     public static function isValid($guid)
@@ -62,7 +61,7 @@ class Guid
     /**
      * Constructor.
      *
-     * @param mixed $value
+     * @param  mixed  $value
      *
      * @throws InvalidArgumentException
      */
@@ -128,8 +127,7 @@ class Guid
     /**
      * Returns the string variant of a binary GUID.
      *
-     * @param string $binary
-     *
+     * @param  string  $binary
      * @return string|null
      */
     protected function binaryGuidToString($binary)
@@ -144,10 +142,9 @@ class Guid
      *
      * @see https://github.com/ldaptools/ldaptools
      *
-     * @param string $hex      The full hex string.
-     * @param array  $sections An array of start and length (unless octet is true, then length is always 2).
-     * @param bool   $octet    Whether this is for octet string form.
-     *
+     * @param  string  $hex  The full hex string.
+     * @param  array  $sections  An array of start and length (unless octet is true, then length is always 2).
+     * @param  bool  $octet  Whether this is for octet string form.
      * @return string The concatenated sections in upper-case.
      */
     protected function parseSection($hex, array $sections, $octet = false)

+ 4 - 8
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/MbString.php

@@ -7,8 +7,7 @@ class MbString
     /**
      * Get the integer value of a specific character.
      *
-     * @param $string
-     *
+     * @param  $string
      * @return int
      */
     public static function ord($string)
@@ -27,8 +26,7 @@ class MbString
     /**
      * Get the character for a specific integer value.
      *
-     * @param $int
-     *
+     * @param  $int
      * @return string
      */
     public static function chr($int)
@@ -43,8 +41,7 @@ class MbString
     /**
      * Split a string into its individual characters and return it as an array.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return string[]
      */
     public static function split($value)
@@ -55,8 +52,7 @@ class MbString
     /**
      * Detects if the given string is UTF 8.
      *
-     * @param $string
-     *
+     * @param  $string
      * @return string|false
      */
     public static function isUtf8($string)

+ 48 - 58
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Password.php

@@ -8,15 +8,14 @@ use ReflectionMethod;
 
 class Password
 {
-    const CRYPT_SALT_TYPE_MD5 = 1;
-    const CRYPT_SALT_TYPE_SHA256 = 5;
-    const CRYPT_SALT_TYPE_SHA512 = 6;
+    public const CRYPT_SALT_TYPE_MD5 = 1;
+    public const CRYPT_SALT_TYPE_SHA256 = 5;
+    public const CRYPT_SALT_TYPE_SHA512 = 6;
 
     /**
      * Make an encoded password for transmission over LDAP.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function encode($password)
@@ -27,9 +26,8 @@ class Password
     /**
      * Make a salted md5 password.
      *
-     * @param string      $password
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  null|string  $salt
      * @return string
      */
     public static function smd5($password, $salt = null)
@@ -40,9 +38,8 @@ class Password
     /**
      * Make a salted SHA password.
      *
-     * @param string      $password
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  null|string  $salt
      * @return string
      */
     public static function ssha($password, $salt = null)
@@ -53,9 +50,8 @@ class Password
     /**
      * Make a salted SSHA256 password.
      *
-     * @param string      $password
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  null|string  $salt
      * @return string
      */
     public static function ssha256($password, $salt = null)
@@ -66,9 +62,8 @@ class Password
     /**
      * Make a salted SSHA384 password.
      *
-     * @param string      $password
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  null|string  $salt
      * @return string
      */
     public static function ssha384($password, $salt = null)
@@ -79,9 +74,8 @@ class Password
     /**
      * Make a salted SSHA512 password.
      *
-     * @param string      $password
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  null|string  $salt
      * @return string
      */
     public static function ssha512($password, $salt = null)
@@ -92,8 +86,7 @@ class Password
     /**
      * Make a non-salted SHA password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function sha($password)
@@ -104,8 +97,7 @@ class Password
     /**
      * Make a non-salted SHA256 password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function sha256($password)
@@ -116,8 +108,7 @@ class Password
     /**
      * Make a non-salted SHA384 password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function sha384($password)
@@ -128,8 +119,7 @@ class Password
     /**
      * Make a non-salted SHA512 password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function sha512($password)
@@ -140,8 +130,7 @@ class Password
     /**
      * Make a non-salted md5 password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string
      */
     public static function md5($password)
@@ -150,11 +139,21 @@ class Password
     }
 
     /**
-     * Crypt password with an MD5 salt.
+     * Make a non-salted NThash password.
      *
-     * @param string $password
-     * @param string $salt
+     * @param  string  $password
+     * @return string
+     */
+    public static function nthash($password)
+    {
+        return '{NTHASH}'.strtoupper(hash('md4', iconv('UTF-8', 'UTF-16LE', $password)));
+    }
+
+    /**
+     * Crypt password with an MD5 salt.
      *
+     * @param  string  $password
+     * @param  string  $salt
      * @return string
      */
     public static function md5Crypt($password, $salt = null)
@@ -165,9 +164,8 @@ class Password
     /**
      * Crypt password with a SHA256 salt.
      *
-     * @param string $password
-     * @param string $salt
-     *
+     * @param  string  $password
+     * @param  string  $salt
      * @return string
      */
     public static function sha256Crypt($password, $salt = null)
@@ -178,9 +176,8 @@ class Password
     /**
      * Crypt a password with a SHA512 salt.
      *
-     * @param string $password
-     * @param string $salt
-     *
+     * @param  string  $password
+     * @param  string  $salt
      * @return string
      */
     public static function sha512Crypt($password, $salt = null)
@@ -191,11 +188,10 @@ class Password
     /**
      * Make a new password hash.
      *
-     * @param string      $password The password to make a hash of.
-     * @param string      $method   The hash function to use.
-     * @param string|null $algo     The algorithm to use for hashing.
-     * @param string|null $salt     The salt to append onto the hash.
-     *
+     * @param  string  $password  The password to make a hash of.
+     * @param  string  $method  The hash function to use.
+     * @param  string|null  $algo  The algorithm to use for hashing.
+     * @param  string|null  $salt  The salt to append onto the hash.
      * @return string
      */
     protected static function makeHash($password, $method, $algo = null, $salt = null)
@@ -208,10 +204,9 @@ class Password
     /**
      * Make a hashed password.
      *
-     * @param string      $password
-     * @param int         $type
-     * @param null|string $salt
-     *
+     * @param  string  $password
+     * @param  int  $type
+     * @param  null|string  $salt
      * @return string
      */
     protected static function makeCrypt($password, $type, $salt = null)
@@ -222,8 +217,7 @@ class Password
     /**
      * Make a salt for the crypt() method using the given type.
      *
-     * @param int $type
-     *
+     * @param  int  $type
      * @return string
      */
     protected static function makeCryptSalt($type)
@@ -242,8 +236,7 @@ class Password
     /**
      * Determine the crypt prefix and length.
      *
-     * @param int $type
-     *
+     * @param  int  $type
      * @return array
      *
      * @throws InvalidArgumentException
@@ -265,8 +258,7 @@ class Password
     /**
      * Attempt to retrieve the hash method used for the password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return string|void
      */
     public static function getHashMethod($password)
@@ -281,8 +273,7 @@ class Password
     /**
      * Attempt to retrieve the hash method and algorithm used for the password.
      *
-     * @param string $password
-     *
+     * @param  string  $password
      * @return array|void
      */
     public static function getHashMethodAndAlgo($password)
@@ -319,8 +310,7 @@ class Password
     /**
      * Determine if the hash method requires a salt to be given.
      *
-     * @param string $method
-     *
+     * @param  string  $method
      * @return bool
      *
      * @throws \ReflectionException

+ 3 - 5
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Sid.php

@@ -17,8 +17,7 @@ class Sid
     /**
      * Determines if the specified SID is valid.
      *
-     * @param string $sid
-     *
+     * @param  string  $sid
      * @return bool
      */
     public static function isValid($sid)
@@ -29,7 +28,7 @@ class Sid
     /**
      * Constructor.
      *
-     * @param mixed $value
+     * @param  mixed  $value
      *
      * @throws InvalidArgumentException
      */
@@ -90,8 +89,7 @@ class Sid
     /**
      * Returns the string variant of a binary SID.
      *
-     * @param string $binary
-     *
+     * @param  string  $binary
      * @return string|null
      */
     protected function binarySidToString($binary)

+ 23 - 33
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSProperty.php

@@ -7,7 +7,7 @@ class TSProperty
     /**
      * Nibble control values. The first value for each is if the nibble is <= 9, otherwise the second value is used.
      */
-    const NIBBLE_CONTROL = [
+    public const NIBBLE_CONTROL = [
         'X' => ['001011', '011010'],
         'Y' => ['001110', '011010'],
     ];
@@ -15,12 +15,12 @@ class TSProperty
     /**
      * The nibble header.
      */
-    const NIBBLE_HEADER = '1110';
+    public const NIBBLE_HEADER = '1110';
 
     /**
      * Conversion factor needed for time values in the TSPropertyArray (stored in microseconds).
      */
-    const TIME_CONVERSION = 60 * 1000;
+    public const TIME_CONVERSION = 60 * 1000;
 
     /**
      * A simple map to help determine how the property needs to be decoded/encoded from/to its binary value.
@@ -85,7 +85,7 @@ class TSProperty
     /**
      * Pass binary TSProperty data to construct its object representation.
      *
-     * @param string|null $value
+     * @param  string|null  $value
      */
     public function __construct($value = null)
     {
@@ -97,8 +97,7 @@ class TSProperty
     /**
      * Set the name for the TSProperty.
      *
-     * @param string $name
-     *
+     * @param  string  $name
      * @return TSProperty
      */
     public function setName($name)
@@ -121,8 +120,7 @@ class TSProperty
     /**
      * Set the value for the TSProperty.
      *
-     * @param string|int $value
-     *
+     * @param  string|int  $value
      * @return TSProperty
      */
     public function setValue($value)
@@ -169,7 +167,7 @@ class TSProperty
     /**
      * Given a TSProperty blob, decode the name/value/type/etc.
      *
-     * @param string $tsProperty
+     * @param  string  $tsProperty
      */
     protected function decode($tsProperty)
     {
@@ -186,9 +184,8 @@ class TSProperty
     /**
      * Based on the property name/value in question, get its encoded form.
      *
-     * @param string     $propName
-     * @param string|int $propValue
-     *
+     * @param  string  $propName
+     * @param  string|int  $propValue
      * @return string
      */
     protected function getEncodedValueForProp($propName, $propValue)
@@ -210,9 +207,8 @@ class TSProperty
     /**
      * Based on the property name in question, get its actual value from the binary blob value.
      *
-     * @param string $propName
-     * @param string $propValue
-     *
+     * @param  string  $propName
+     * @param  string  $propValue
      * @return string|int
      */
     protected function getDecodedValueForProp($propName, $propValue)
@@ -238,9 +234,8 @@ class TSProperty
      * Decode the property by inspecting the nibbles of each blob, checking
      * the control, and adding up the results into a final value.
      *
-     * @param string $hex
-     * @param bool   $string Whether or not this is simple string data.
-     *
+     * @param  string  $hex
+     * @param  bool  $string  Whether or not this is simple string data.
      * @return string
      */
     protected function decodePropValue($hex, $string = false)
@@ -272,9 +267,8 @@ class TSProperty
     /**
      * Get the encoded property value as a binary blob.
      *
-     * @param string $value
-     * @param bool   $string
-     *
+     * @param  string  $value
+     * @param  bool  $string
      * @return string
      */
     protected function encodePropValue($value, $string = false)
@@ -314,9 +308,8 @@ class TSProperty
      * a workaround that turns a literal bit-string into a
      * packed byte-string with 8 bits per byte.
      *
-     * @param string $bits
-     * @param bool   $len
-     *
+     * @param  string  $bits
+     * @param  bool  $len
      * @return string
      */
     protected function packBitString($bits, $len)
@@ -337,9 +330,8 @@ class TSProperty
     /**
      * Based on the control, adjust the nibble accordingly.
      *
-     * @param string $nibble
-     * @param string $control
-     *
+     * @param  string  $nibble
+     * @param  string  $control
      * @return string
      */
     protected function nibbleControl($nibble, $control)
@@ -362,9 +354,8 @@ class TSProperty
      * the control for X or Y equals 011010. Additionally, if the dec value of the nibble is > 9, then the nibble value
      * must be subtracted by 9 before the final value is constructed.
      *
-     * @param string $nibbleType Either X or Y
-     * @param string $nibble
-     *
+     * @param  string  $nibbleType  Either X or Y
+     * @param  string  $nibble
      * @return string
      */
     protected function getNibbleWithControl($nibbleType, $nibble)
@@ -384,9 +375,8 @@ class TSProperty
     /**
      * Need to make sure hex values are always an even length, so pad as needed.
      *
-     * @param int $int
-     * @param int $padLength The hex string must be padded to this length (with zeros).
-     *
+     * @param  int  $int
+     * @param  int  $padLength  The hex string must be padded to this length (with zeros).
      * @return string
      */
     protected function dec2hex($int, $padLength = 2)

+ 15 - 23
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/TSPropertyArray.php

@@ -9,14 +9,14 @@ class TSPropertyArray
     /**
      * Represents that the TSPropertyArray data is valid.
      */
-    const VALID_SIGNATURE = 'P';
+    public const VALID_SIGNATURE = 'P';
 
     /**
      * The default values for the TSPropertyArray structure.
      *
      * @var array
      */
-    const DEFAULTS = [
+    public const DEFAULTS = [
         'CtxCfgPresent' => 2953518677,
         'CtxWFProfilePath' => '',
         'CtxWFProfilePathW' => '',
@@ -71,7 +71,7 @@ class TSPropertyArray
      *   - Pass the userParameters binary value. The object representation of that will be decoded and constructed.
      *   - Pass nothing and a default set of TSProperty key => value pairs will be used (See DEFAULTS constant).
      *
-     * @param mixed $tsPropertyArray
+     * @param  mixed  $tsPropertyArray
      */
     public function __construct($tsPropertyArray = null)
     {
@@ -93,8 +93,7 @@ class TSPropertyArray
     /**
      * Check if a specific TSProperty exists by its property name.
      *
-     * @param string $propName
-     *
+     * @param  string  $propName
      * @return bool
      */
     public function has($propName)
@@ -105,8 +104,7 @@ class TSPropertyArray
     /**
      * Get a TSProperty object by its property name (ie. CtxWFProfilePath).
      *
-     * @param string $propName
-     *
+     * @param  string  $propName
      * @return TSProperty
      */
     public function get($propName)
@@ -119,8 +117,7 @@ class TSPropertyArray
     /**
      * Add a TSProperty object. If it already exists, it will be overwritten.
      *
-     * @param TSProperty $tsProperty
-     *
+     * @param  TSProperty  $tsProperty
      * @return $this
      */
     public function add(TSProperty $tsProperty)
@@ -133,8 +130,7 @@ class TSPropertyArray
     /**
      * Remove a TSProperty by its property name (ie. CtxMinEncryptionLevel).
      *
-     * @param string $propName
-     *
+     * @param  string  $propName
      * @return $this
      */
     public function remove($propName)
@@ -151,9 +147,8 @@ class TSPropertyArray
     /**
      * Set the value for a specific TSProperty by its name.
      *
-     * @param string $propName
-     * @param mixed  $propValue
-     *
+     * @param  string  $propName
+     * @param  mixed  $propValue
      * @return $this
      */
     public function set($propName, $propValue)
@@ -214,7 +209,7 @@ class TSPropertyArray
     /**
      * Validates that the given property name exists.
      *
-     * @param string $propName
+     * @param  string  $propName
      */
     protected function validateProp($propName)
     {
@@ -224,8 +219,7 @@ class TSPropertyArray
     }
 
     /**
-     * @param string $propName
-     *
+     * @param  string  $propName
      * @return TSProperty
      */
     protected function getTsPropObj($propName)
@@ -236,8 +230,7 @@ class TSPropertyArray
     /**
      * Get an associative array with all of the userParameters property names and values.
      *
-     * @param string $userParameters
-     *
+     * @param  string  $userParameters
      * @return void
      */
     protected function decodeUserParameters($userParameters)
@@ -260,7 +253,7 @@ class TSPropertyArray
         // Reserved data length + (count and sig length == 4) + the added lengths of the TSPropertyArray
         // This saves anything after that variable TSPropertyArray data, so as to not squash anything stored there
         if (strlen($userParameters) > (96 + 4 + $length)) {
-            $this->postBinary = hex2bin(substr($userParameters, (96 + 4 + $length)));
+            $this->postBinary = hex2bin(substr($userParameters, 96 + 4 + $length));
         }
     }
 
@@ -270,9 +263,8 @@ class TSPropertyArray
      * individual TSProperty structures. Return the full length
      * of the TSPropertyArray data.
      *
-     * @param string $tsPropertyArray
-     * @param int    $tsPropCount
-     *
+     * @param  string  $tsPropertyArray
+     * @param  int  $tsPropCount
      * @return int The length of the data in the TSPropertyArray
      */
     protected function addTSPropData($tsPropertyArray, $tsPropCount)

+ 30 - 29
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Attributes/Timestamp.php

@@ -5,11 +5,14 @@ namespace LdapRecord\Models\Attributes;
 use Carbon\Carbon;
 use Carbon\CarbonInterface;
 use DateTime;
+use DateTimeZone;
 use LdapRecord\LdapRecordException;
 use LdapRecord\Utilities;
 
 class Timestamp
 {
+    public const WINDOWS_INT_MAX = 9223372036854775807;
+
     /**
      * The current timestamp type.
      *
@@ -31,7 +34,7 @@ class Timestamp
     /**
      * Constructor.
      *
-     * @param string $type
+     * @param  string  $type
      *
      * @throws LdapRecordException
      */
@@ -43,7 +46,7 @@ class Timestamp
     /**
      * Set the type of timestamp to convert from / to.
      *
-     * @param string $type
+     * @param  string  $type
      *
      * @throws LdapRecordException
      */
@@ -59,8 +62,7 @@ class Timestamp
     /**
      * Converts the value to an LDAP date string.
      *
-     * @param mixed $value
-     *
+     * @param  mixed  $value
      * @return float|string
      *
      * @throws LdapRecordException
@@ -107,21 +109,19 @@ class Timestamp
     /**
      * Determine if the value given is in Windows Integer (NTFS Filetime) format.
      *
-     * @param int|string $value
-     *
+     * @param  int|string  $value
      * @return bool
      */
     protected function valueIsWindowsIntegerType($value)
     {
-        return is_numeric($value) && strlen((string) $value) === 18;
+        return is_numeric($value) && in_array(strlen((string) $value), [18, 19]);
     }
 
     /**
      * Converts the LDAP timestamp value to a Carbon instance.
      *
-     * @param mixed $value
-     *
-     * @return Carbon|false
+     * @param  mixed  $value
+     * @return Carbon|int|false
      *
      * @throws LdapRecordException
      */
@@ -153,14 +153,13 @@ class Timestamp
     /**
      * Converts standard LDAP timestamps to a date time object.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return DateTime|false
      */
     protected function convertLdapTimeToDateTime($value)
     {
         return DateTime::createFromFormat(
-            strpos($value, 'Z') !== false ? 'YmdHis\Z' : 'YmdHisT',
+            str_contains((string) $value, 'Z') ? 'YmdHis\Z' : 'YmdHisT',
             $value
         );
     }
@@ -168,8 +167,7 @@ class Timestamp
     /**
      * Converts date objects to a standard LDAP timestamp.
      *
-     * @param DateTime $date
-     *
+     * @param  DateTime  $date
      * @return string
      */
     protected function convertDateTimeToLdapTime(DateTime $date)
@@ -182,23 +180,22 @@ class Timestamp
     /**
      * Converts standard windows timestamps to a date time object.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return DateTime|false
      */
     protected function convertWindowsTimeToDateTime($value)
     {
         return DateTime::createFromFormat(
-            strpos($value, '0Z') !== false ? 'YmdHis.0\Z' : 'YmdHis.0T',
-            $value
+            str_contains((string) $value, '0Z') ? 'YmdHis.0\Z' : 'YmdHis.0T',
+            $value,
+            new DateTimeZone('UTC')
         );
     }
 
     /**
      * Converts date objects to a windows timestamp.
      *
-     * @param DateTime $date
-     *
+     * @param  DateTime  $date
      * @return string
      */
     protected function convertDateTimeToWindows(DateTime $date)
@@ -211,20 +208,25 @@ class Timestamp
     /**
      * Converts standard windows integer dates to a date time object.
      *
-     * @param int $value
-     *
-     * @return DateTime|false
+     * @param  int  $value
+     * @return DateTime|int|false
      *
      * @throws \Exception
      */
     protected function convertWindowsIntegerTimeToDateTime($value)
     {
-        // ActiveDirectory dates that contain integers may return
-        // "0" when they are not set. We will validate that here.
-        if (! $value) {
+        if (is_null($value) || $value === '') {
             return false;
         }
 
+        if ($value == 0) {
+            return (int) $value;
+        }
+
+        if ($value == static::WINDOWS_INT_MAX) {
+            return (int) $value;
+        }
+
         return (new DateTime())->setTimestamp(
             Utilities::convertWindowsTimeToUnixTime($value)
         );
@@ -233,8 +235,7 @@ class Timestamp
     /**
      * Converts date objects to a windows integer timestamp.
      *
-     * @param DateTime $date
-     *
+     * @param  DateTime  $date
      * @return float
      */
     protected function convertDateTimeToWindowsInteger(DateTime $date)

+ 13 - 19
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/BatchModification.php

@@ -11,9 +11,9 @@ class BatchModification
     /**
      * The array keys to be used in batch modifications.
      */
-    const KEY_ATTRIB = 'attrib';
-    const KEY_MODTYPE = 'modtype';
-    const KEY_VALUES = 'values';
+    public const KEY_ATTRIB = 'attrib';
+    public const KEY_MODTYPE = 'modtype';
+    public const KEY_VALUES = 'values';
 
     /**
      * The attribute of the modification.
@@ -46,9 +46,9 @@ class BatchModification
     /**
      * Constructor.
      *
-     * @param string|null     $attribute
-     * @param string|int|null $type
-     * @param array           $values
+     * @param  string|null  $attribute
+     * @param  string|int|null  $type
+     * @param  array  $values
      */
     public function __construct($attribute = null, $type = null, array $values = [])
     {
@@ -60,8 +60,7 @@ class BatchModification
     /**
      * Set the original value of the attribute before modification.
      *
-     * @param array|string $original
-     *
+     * @param  array|string  $original
      * @return $this
      */
     public function setOriginal($original = [])
@@ -84,8 +83,7 @@ class BatchModification
     /**
      * Set the attribute of the modification.
      *
-     * @param string $attribute
-     *
+     * @param  string  $attribute
      * @return $this
      */
     public function setAttribute($attribute)
@@ -108,8 +106,7 @@ class BatchModification
     /**
      * Set the values of the modification.
      *
-     * @param array $values
-     *
+     * @param  array  $values
      * @return $this
      */
     public function setValues(array $values = [])
@@ -127,8 +124,7 @@ class BatchModification
     /**
      * Normalize all of the attribute values.
      *
-     * @param array|string $values
-     *
+     * @param  array|string  $values
      * @return array
      */
     protected function normalizeAttributeValues($values = [])
@@ -152,8 +148,7 @@ class BatchModification
     /**
      * Set the type of the modification.
      *
-     * @param int|null $type
-     *
+     * @param  int|null  $type
      * @return $this
      */
     public function setType($type = null)
@@ -207,7 +202,7 @@ class BatchModification
             case empty($this->original) && ! empty($this->values):
                 return $this->setType(LDAP_MODIFY_BATCH_ADD);
             default:
-               return $this->determineBatchTypeFromOriginal();
+                return $this->determineBatchTypeFromOriginal();
         }
     }
 
@@ -291,8 +286,7 @@ class BatchModification
     /**
      * Determines if the given modtype is valid.
      *
-     * @param int $type
-     *
+     * @param  int  $type
      * @return bool
      */
     protected function isValidType($type)

+ 22 - 15
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Collection.php

@@ -10,10 +10,21 @@ use LdapRecord\Support\Arr;
 class Collection extends QueryCollection
 {
     /**
-     * Determine if the collection contains all of the given models, or any models.
+     * Get a collection of the model's distinguished names.
      *
-     * @param mixed $models
+     * @return static
+     */
+    public function modelDns()
+    {
+        return $this->map(function (Model $model) {
+            return $model->getDn();
+        });
+    }
+
+    /**
+     * Determine if the collection contains all of the given models, or any models.
      *
+     * @param  mixed  $models
      * @return bool
      */
     public function exists($models = null)
@@ -47,10 +58,9 @@ class Collection extends QueryCollection
     /**
      * Determine if any of the given models are contained in the collection.
      *
-     * @param mixed $key
-     * @param mixed $operator
-     * @param mixed $value
-     *
+     * @param  mixed  $key
+     * @param  mixed  $operator
+     * @param  mixed  $value
      * @return bool
      */
     public function contains($key, $operator = null, $value = null)
@@ -78,8 +88,7 @@ class Collection extends QueryCollection
     /**
      * Get the provided models as an array.
      *
-     * @param mixed $models
-     *
+     * @param  mixed  $models
      * @return array
      */
     protected function getArrayableModels($models = null)
@@ -92,17 +101,16 @@ class Collection extends QueryCollection
     /**
      * Compare the related model with the given.
      *
-     * @param Model|string $model
-     * @param Model        $related
-     *
+     * @param  Model|string  $model
+     * @param  Model  $related
      * @return bool
      */
     protected function compareModelWithRelated($model, $related)
     {
         if (is_string($model)) {
             return $this->isValidDn($model)
-                ? $related->getDn() == $model
-                : $related->getName() == $model;
+                ? strcasecmp($related->getDn(), $model) === 0
+                : strcasecmp($related->getName(), $model) === 0;
         }
 
         return $related->is($model);
@@ -111,8 +119,7 @@ class Collection extends QueryCollection
     /**
      * Determine if the given string is a valid distinguished name.
      *
-     * @param string $dn
-     *
+     * @param  string  $dn
      * @return bool
      */
     protected function isValidDn($dn)

+ 3 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/CanAuthenticate.php

@@ -2,6 +2,7 @@
 
 namespace LdapRecord\Models\Concerns;
 
+/** @mixin \LdapRecord\Models\Model */
 trait CanAuthenticate
 {
     /**
@@ -17,7 +18,7 @@ trait CanAuthenticate
     /**
      * Get the unique identifier for the user.
      *
-     * @return mixed
+     * @return string
      */
     public function getAuthIdentifier()
     {
@@ -47,8 +48,7 @@ trait CanAuthenticate
     /**
      * Set the token value for the "remember me" session.
      *
-     * @param string $value
-     *
+     * @param  string  $value
      * @return void
      */
     public function setRememberToken($value)

+ 162 - 114
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasAttributes.php

@@ -74,6 +74,18 @@ trait HasAttributes
      */
     protected static $mutatorCache = [];
 
+    /**
+     * Convert the model's original attributes to an array.
+     *
+     * @return array
+     */
+    public function originalToArray()
+    {
+        return $this->encodeAttributes(
+            $this->convertAttributesForJson($this->original)
+        );
+    }
+
     /**
      * Convert the model's attributes to an array.
      *
@@ -112,10 +124,37 @@ trait HasAttributes
     }
 
     /**
-     * Add the date attributes to the attributes array.
+     * Convert the model's serialized original attributes to their original form.
+     *
+     * @param  array  $attributes
+     * @return array
+     */
+    public function arrayToOriginal(array $attributes)
+    {
+        return $this->decodeAttributes(
+            $this->convertAttributesFromJson($attributes)
+        );
+    }
+
+    /**
+     * Convert the model's serialized attributes to their original form.
      *
-     * @param array $attributes
+     * @param  array  $attributes
+     * @return array
+     */
+    public function arrayToAttributes(array $attributes)
+    {
+        $attributes = $this->restoreDateAttributesFromArray($attributes);
+
+        return $this->decodeAttributes(
+            $this->convertAttributesFromJson($attributes)
+        );
+    }
+
+    /**
+     * Add the date attributes to the attributes array.
      *
+     * @param  array  $attributes
      * @return array
      */
     protected function addDateAttributesToArray(array $attributes)
@@ -136,10 +175,30 @@ trait HasAttributes
     }
 
     /**
-     * Prepare a date for array / JSON serialization.
+     * Restore the date attributes to their true value from serialized attributes.
      *
-     * @param DateTimeInterface $date
+     * @param  array  $attributes
+     * @return array
+     */
+    protected function restoreDateAttributesFromArray(array $attributes)
+    {
+        foreach ($this->getDates() as $attribute => $type) {
+            if (! isset($attributes[$attribute])) {
+                continue;
+            }
+
+            $date = $this->fromDateTime($type, $attributes[$attribute]);
+
+            $attributes[$attribute] = Arr::wrap($date);
+        }
+
+        return $attributes;
+    }
+
+    /**
+     * Prepare a date for array / JSON serialization.
      *
+     * @param  DateTimeInterface  $date
      * @return string
      */
     protected function serializeDate(DateTimeInterface $date)
@@ -162,10 +221,24 @@ trait HasAttributes
     }
 
     /**
-     * Encode the given value for proper serialization.
+     * Recursively UTF-8 decode the given attributes.
      *
-     * @param string $value
+     * @param  array  $attributes
+     * @return array
+     */
+    public function decodeAttributes($attributes)
+    {
+        array_walk_recursive($attributes, function (&$value) {
+            $value = $this->decodeValue($value);
+        });
+
+        return $attributes;
+    }
+
+    /**
+     * Encode the value for serialization.
      *
+     * @param  string  $value
      * @return string
      */
     protected function encodeValue($value)
@@ -173,15 +246,33 @@ trait HasAttributes
         // If we are able to detect the encoding, we will
         // encode only the attributes that need to be,
         // so that we do not double encode values.
-        return MbString::isLoaded() && MbString::isUtf8($value) ? $value : utf8_encode($value);
+        if (MbString::isLoaded() && MbString::isUtf8($value)) {
+            return $value;
+        }
+
+        return utf8_encode($value);
     }
 
     /**
-     * Add the mutated attributes to the attributes array.
+     * Decode the value from serialization.
      *
-     * @param array $attributes
-     * @param array $mutatedAttributes
+     * @param  string  $value
+     * @return string
+     */
+    protected function decodeValue($value)
+    {
+        if (MbString::isLoaded() && MbString::isUtf8($value)) {
+            return mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
+        }
+
+        return $value;
+    }
+
+    /**
+     * Add the mutated attributes to the attributes array.
      *
+     * @param  array  $attributes
+     * @param  array  $mutatedAttributes
      * @return array
      */
     protected function addMutatedAttributesToArray(array $attributes, array $mutatedAttributes)
@@ -221,8 +312,7 @@ trait HasAttributes
     /**
      * Fills the entry with the supplied attributes.
      *
-     * @param array $attributes
-     *
+     * @param  array  $attributes
      * @return $this
      */
     public function fill(array $attributes = [])
@@ -237,9 +327,8 @@ trait HasAttributes
     /**
      * Returns the models attribute by its key.
      *
-     * @param int|string $key
-     * @param mixed      $default
-     *
+     * @param  int|string  $key
+     * @param  mixed  $default
      * @return mixed
      */
     public function getAttribute($key, $default = null)
@@ -254,9 +343,8 @@ trait HasAttributes
     /**
      * Get an attributes value.
      *
-     * @param string $key
-     * @param mixed  $default
-     *
+     * @param  string  $key
+     * @param  mixed  $default
      * @return mixed
      */
     public function getAttributeValue($key, $default = null)
@@ -282,8 +370,7 @@ trait HasAttributes
     /**
      * Determine if the given attribute is a date.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     public function isDateAttribute($key)
@@ -310,9 +397,8 @@ trait HasAttributes
     /**
      * Convert the given date value to an LDAP compatible value.
      *
-     * @param string $type
-     * @param mixed  $value
-     *
+     * @param  string  $type
+     * @param  mixed  $value
      * @return float|string
      *
      * @throws LdapRecordException
@@ -325,9 +411,8 @@ trait HasAttributes
     /**
      * Convert the given LDAP date value to a Carbon instance.
      *
-     * @param mixed  $value
-     * @param string $type
-     *
+     * @param  mixed  $value
+     * @param  string  $type
      * @return Carbon|false
      *
      * @throws LdapRecordException
@@ -340,9 +425,8 @@ trait HasAttributes
     /**
      * Determine whether an attribute should be cast to a native type.
      *
-     * @param string            $key
-     * @param array|string|null $types
-     *
+     * @param  string  $key
+     * @param  array|string|null  $types
      * @return bool
      */
     public function hasCast($key, $types = null)
@@ -367,8 +451,7 @@ trait HasAttributes
     /**
      * Determine whether a value is JSON castable for inbound manipulation.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     protected function isJsonCastable($key)
@@ -379,8 +462,7 @@ trait HasAttributes
     /**
      * Get the type of cast for a model attribute.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return string
      */
     protected function getCastType($key)
@@ -399,8 +481,7 @@ trait HasAttributes
     /**
      * Determine if the cast is a decimal.
      *
-     * @param string $cast
-     *
+     * @param  string  $cast
      * @return bool
      */
     protected function isDecimalCast($cast)
@@ -411,8 +492,7 @@ trait HasAttributes
     /**
      * Determine if the cast is a datetime.
      *
-     * @param string $cast
-     *
+     * @param  string  $cast
      * @return bool
      */
     protected function isDateTimeCast($cast)
@@ -423,8 +503,7 @@ trait HasAttributes
     /**
      * Determine if the given attribute must be casted.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     protected function isCastedAttribute($key)
@@ -435,9 +514,8 @@ trait HasAttributes
     /**
      * Cast an attribute to a native PHP type.
      *
-     * @param string     $key
-     * @param array|null $value
-     *
+     * @param  string  $key
+     * @param  array|null  $value
      * @return mixed
      */
     protected function castAttribute($key, $value)
@@ -490,9 +568,8 @@ trait HasAttributes
     /**
      * Cast the given attribute to JSON.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return string
      */
     protected function castAttributeAsJson($key, $value)
@@ -522,8 +599,7 @@ trait HasAttributes
     /**
      * Encode the given value as JSON.
      *
-     * @param mixed $value
-     *
+     * @param  mixed  $value
      * @return string
      */
     protected function asJson($value)
@@ -534,9 +610,8 @@ trait HasAttributes
     /**
      * Decode the given JSON back into an array or object.
      *
-     * @param string $value
-     * @param bool   $asObject
-     *
+     * @param  string  $value
+     * @param  bool  $asObject
      * @return mixed
      */
     public function fromJson($value, $asObject = false)
@@ -547,8 +622,7 @@ trait HasAttributes
     /**
      * Decode the given float.
      *
-     * @param mixed $value
-     *
+     * @param  mixed  $value
      * @return mixed
      */
     public function fromFloat($value)
@@ -568,8 +642,7 @@ trait HasAttributes
     /**
      * Cast the value to a boolean.
      *
-     * @param mixed $value
-     *
+     * @param  mixed  $value
      * @return bool
      */
     protected function asBoolean($value)
@@ -582,9 +655,8 @@ trait HasAttributes
     /**
      * Cast a decimal value as a string.
      *
-     * @param float $value
-     * @param int   $decimals
-     *
+     * @param  float  $value
+     * @param  int  $decimals
      * @return string
      */
     protected function asDecimal($value, $decimals)
@@ -605,8 +677,7 @@ trait HasAttributes
     /**
      * Get an attribute array of all arrayable values.
      *
-     * @param array $values
-     *
+     * @param  array  $values
      * @return array
      */
     protected function getArrayableItems(array $values)
@@ -651,8 +722,7 @@ trait HasAttributes
     /**
      * Set the date format used by the model for serialization.
      *
-     * @param string $format
-     *
+     * @param  string  $format
      * @return $this
      */
     public function setDateFormat($format)
@@ -665,8 +735,7 @@ trait HasAttributes
     /**
      * Get an attribute from the $attributes array.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return mixed
      */
     protected function getAttributeFromArray($key)
@@ -687,9 +756,8 @@ trait HasAttributes
     /**
      * Returns the first attribute by the specified key.
      *
-     * @param string $key
-     * @param mixed  $default
-     *
+     * @param  string  $key
+     * @param  mixed  $default
      * @return mixed
      */
     public function getFirstAttribute($key, $default = null)
@@ -712,9 +780,8 @@ trait HasAttributes
     /**
      * Set an attribute value by the specified key.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return $this
      */
     public function setAttribute($key, $value)
@@ -743,9 +810,8 @@ trait HasAttributes
     /**
      * Set an attribute on the model. No checking is done.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return $this
      */
     public function setRawAttribute($key, $value)
@@ -760,9 +826,8 @@ trait HasAttributes
     /**
      * Set the models first attribute value.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return $this
      */
     public function setFirstAttribute($key, $value)
@@ -773,9 +838,8 @@ trait HasAttributes
     /**
      * Add a unique value to the given attribute.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return $this
      */
     public function addAttributeValue($key, $value)
@@ -791,8 +855,7 @@ trait HasAttributes
     /**
      * Determine if a get mutator exists for an attribute.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     public function hasGetMutator($key)
@@ -803,8 +866,7 @@ trait HasAttributes
     /**
      * Determine if a set mutator exists for an attribute.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     public function hasSetMutator($key)
@@ -815,9 +877,8 @@ trait HasAttributes
     /**
      * Set the value of an attribute using its mutator.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return mixed
      */
     protected function setMutatedAttributeValue($key, $value)
@@ -828,9 +889,8 @@ trait HasAttributes
     /**
      * Get the value of an attribute using its mutator.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return mixed
      */
     protected function getMutatedAttributeValue($key, $value)
@@ -843,8 +903,7 @@ trait HasAttributes
      *
      * Hyphenated attributes will use pascal cased methods.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return mixed
      */
     protected function getMutatorMethodName($key)
@@ -857,9 +916,8 @@ trait HasAttributes
     /**
      * Get the value of an attribute using its mutator for array conversion.
      *
-     * @param string $key
-     * @param mixed  $value
-     *
+     * @param  string  $key
+     * @param  mixed  $value
      * @return array
      */
     protected function mutateAttributeForArray($key, $value)
@@ -874,8 +932,7 @@ trait HasAttributes
      *
      * Used when constructing an existing LDAP record.
      *
-     * @param array $attributes
-     *
+     * @param  array  $attributes
      * @return $this
      */
     public function setRawAttributes(array $attributes = [])
@@ -915,9 +972,8 @@ trait HasAttributes
     /**
      * Filters the count key recursively from raw LDAP attributes.
      *
-     * @param array $attributes
-     * @param array $keys
-     *
+     * @param  array  $attributes
+     * @param  array  $keys
      * @return array
      */
     public function filterRawAttributes(array $attributes = [], array $keys = ['count', 'dn'])
@@ -938,8 +994,7 @@ trait HasAttributes
     /**
      * Determine if the model has the given attribute.
      *
-     * @param int|string $key
-     *
+     * @param  int|string  $key
      * @return bool
      */
     public function hasAttribute($key)
@@ -991,8 +1046,7 @@ trait HasAttributes
     /**
      * Determine if the given attribute is dirty.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     public function isDirty($key)
@@ -1013,8 +1067,7 @@ trait HasAttributes
     /**
      * Set the accessors to append to model arrays.
      *
-     * @param array $appends
-     *
+     * @param  array  $appends
      * @return $this
      */
     public function setAppends(array $appends)
@@ -1027,8 +1080,7 @@ trait HasAttributes
     /**
      * Return whether the accessor attribute has been appended.
      *
-     * @param string $attribute
-     *
+     * @param  string  $attribute
      * @return bool
      */
     public function hasAppended($attribute)
@@ -1039,8 +1091,7 @@ trait HasAttributes
     /**
      * Returns a normalized attribute key.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return string
      */
     public function normalizeAttributeKey($key)
@@ -1056,8 +1107,7 @@ trait HasAttributes
     /**
      * Determine if the new and old values for a given key are equivalent.
      *
-     * @param string $key
-     *
+     * @param  string  $key
      * @return bool
      */
     protected function originalIsEquivalent($key)
@@ -1097,8 +1147,7 @@ trait HasAttributes
     /**
      * Extract and cache all the mutated attributes of a class.
      *
-     * @param string $class
-     *
+     * @param  string  $class
      * @return void
      */
     public static function cacheMutatedAttributes($class)
@@ -1113,8 +1162,7 @@ trait HasAttributes
     /**
      * Get all of the attribute mutator methods.
      *
-     * @param mixed $class
-     *
+     * @param  mixed  $class
      * @return array
      */
     protected static function getMutatorMethods($class)

+ 36 - 8
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasEvents.php

@@ -4,15 +4,17 @@ namespace LdapRecord\Models\Concerns;
 
 use Closure;
 use LdapRecord\Events\NullDispatcher;
+use LdapRecord\Models\Events;
 use LdapRecord\Models\Events\Event;
+use LdapRecord\Support\Arr;
 
+/** @mixin \LdapRecord\Models\Model */
 trait HasEvents
 {
     /**
      * Execute the callback without raising any events.
      *
-     * @param Closure $callback
-     *
+     * @param  Closure  $callback
      * @return mixed
      */
     protected static function withoutEvents(Closure $callback)
@@ -37,10 +39,37 @@ trait HasEvents
     }
 
     /**
-     * Fires the specified model event.
+     * Dispatch the given model events.
+     *
+     * @param  string|array  $events
+     * @param  array  $args
+     * @return void
+     */
+    protected function dispatch($events, array $args = [])
+    {
+        foreach (Arr::wrap($events) as $name) {
+            $this->fireCustomModelEvent($name, $args);
+        }
+    }
+
+    /**
+     * Fire a custom model event.
      *
-     * @param Event $event
+     * @param  string  $name
+     * @param  array  $args
+     * @return mixed
+     */
+    protected function fireCustomModelEvent($name, array $args = [])
+    {
+        $event = implode('\\', [Events::class, ucfirst($name)]);
+
+        return $this->fireModelEvent(new $event($this, ...$args));
+    }
+
+    /**
+     * Fire a model event.
      *
+     * @param  Event  $event
      * @return mixed
      */
     protected function fireModelEvent(Event $event)
@@ -49,11 +78,10 @@ trait HasEvents
     }
 
     /**
-     * Listens to a model event.
-     *
-     * @param string  $event
-     * @param Closure $listener
+     * Listen to a model event.
      *
+     * @param  string  $event
+     * @param  Closure  $listener
      * @return mixed
      */
     protected function listenForModelEvent($event, Closure $listener)

+ 5 - 7
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasGlobalScopes.php

@@ -6,14 +6,14 @@ use Closure;
 use InvalidArgumentException;
 use LdapRecord\Models\Scope;
 
+/** @mixin \LdapRecord\Models\Model */
 trait HasGlobalScopes
 {
     /**
      * Register a new global scope on the model.
      *
-     * @param Scope|Closure|string $scope
-     * @param Closure|null         $implementation
-     *
+     * @param  Scope|Closure|string  $scope
+     * @param  Closure|null  $implementation
      * @return mixed
      *
      * @throws InvalidArgumentException
@@ -34,8 +34,7 @@ trait HasGlobalScopes
     /**
      * Determine if a model has a global scope.
      *
-     * @param Scope|string $scope
-     *
+     * @param  Scope|string  $scope
      * @return bool
      */
     public static function hasGlobalScope($scope)
@@ -46,8 +45,7 @@ trait HasGlobalScopes
     /**
      * Get a global scope registered with the model.
      *
-     * @param Scope|string $scope
-     *
+     * @param  Scope|string  $scope
      * @return Scope|Closure|null
      */
     public static function getGlobalScope($scope)

+ 12 - 15
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasPassword.php

@@ -6,12 +6,13 @@ use LdapRecord\ConnectionException;
 use LdapRecord\LdapRecordException;
 use LdapRecord\Models\Attributes\Password;
 
+/** @mixin \LdapRecord\Models\Model */
 trait HasPassword
 {
     /**
      * Set the password on the user.
      *
-     * @param string|array $password
+     * @param  string|array  $password
      *
      * @throws ConnectionException
      */
@@ -48,7 +49,7 @@ trait HasPassword
     /**
      * Alias for setting the password on the user.
      *
-     * @param string|array $password
+     * @param  string|array  $password
      *
      * @throws ConnectionException
      */
@@ -106,10 +107,9 @@ trait HasPassword
     /**
      * Set the changed password.
      *
-     * @param string $oldPassword
-     * @param string $newPassword
-     * @param string $attribute
-     *
+     * @param  string  $oldPassword
+     * @param  string  $newPassword
+     * @param  string  $attribute
      * @return void
      */
     protected function setChangedPassword($oldPassword, $newPassword, $attribute)
@@ -136,9 +136,8 @@ trait HasPassword
     /**
      * Set the password on the model.
      *
-     * @param string $password
-     * @param string $attribute
-     *
+     * @param  string  $password
+     * @param  string  $attribute
      * @return void
      */
     protected function setPassword($password, $attribute)
@@ -155,10 +154,9 @@ trait HasPassword
     /**
      * Encode / hash the given password.
      *
-     * @param string $method
-     * @param string $password
-     * @param string $salt
-     *
+     * @param  string  $method
+     * @param  string  $password
+     * @param  string  $salt
      * @return string
      *
      * @throws LdapRecordException
@@ -203,8 +201,7 @@ trait HasPassword
     /**
      * Attempt to retrieve the password's salt.
      *
-     * @param string $method
-     *
+     * @param  string  $method
      * @return string|null
      */
     public function getPasswordSalt($method)

+ 33 - 12
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasRelationships.php

@@ -5,6 +5,7 @@ namespace LdapRecord\Models\Concerns;
 use LdapRecord\Models\Relations\HasMany;
 use LdapRecord\Models\Relations\HasManyIn;
 use LdapRecord\Models\Relations\HasOne;
+use LdapRecord\Models\Relations\Relation;
 use LdapRecord\Support\Arr;
 
 trait HasRelationships
@@ -12,10 +13,9 @@ trait HasRelationships
     /**
      * Returns a new has one relationship.
      *
-     * @param mixed  $related
-     * @param string $relationKey
-     * @param string $foreignKey
-     *
+     * @param  mixed  $related
+     * @param  string  $relationKey
+     * @param  string  $foreignKey
      * @return HasOne
      */
     public function hasOne($related, $relationKey, $foreignKey = 'dn')
@@ -26,10 +26,9 @@ trait HasRelationships
     /**
      * Returns a new has many relationship.
      *
-     * @param mixed  $related
-     * @param string $relationKey
-     * @param string $foreignKey
-     *
+     * @param  mixed  $related
+     * @param  string  $relationKey
+     * @param  string  $foreignKey
      * @return HasMany
      */
     public function hasMany($related, $relationKey, $foreignKey = 'dn')
@@ -40,10 +39,9 @@ trait HasRelationships
     /**
      * Returns a new has many in relationship.
      *
-     * @param mixed  $related
-     * @param string $relationKey
-     * @param string $foreignKey
-     *
+     * @param  mixed  $related
+     * @param  string  $relationKey
+     * @param  string  $foreignKey
      * @return HasManyIn
      */
     public function hasManyIn($related, $relationKey, $foreignKey = 'dn')
@@ -51,6 +49,29 @@ trait HasRelationships
         return new HasManyIn($this->newQuery(), $this, $related, $relationKey, $foreignKey, $this->guessRelationshipName());
     }
 
+    /**
+     * Get a relationship by its name.
+     *
+     * @param  string  $relationName
+     * @return Relation|null
+     */
+    public function getRelation($relationName)
+    {
+        if (! method_exists($this, $relationName)) {
+            return;
+        }
+
+        if (! $relation = $this->{$relationName}()) {
+            return;
+        }
+
+        if (! $relation instanceof Relation) {
+            return;
+        }
+
+        return $relation;
+    }
+
     /**
      * Get the relationships name.
      *

+ 1 - 0
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HasScopes.php

@@ -2,6 +2,7 @@
 
 namespace LdapRecord\Models\Concerns;
 
+/** @mixin \LdapRecord\Models\Model */
 trait HasScopes
 {
     /**

+ 8 - 12
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/HidesAttributes.php

@@ -6,6 +6,8 @@ namespace LdapRecord\Models\Concerns;
  * @author Taylor Otwell
  *
  * @see https://laravel.com
+ *
+ * @mixin \LdapRecord\Models\Model
  */
 trait HidesAttributes
 {
@@ -38,8 +40,7 @@ trait HidesAttributes
     /**
      * Set the hidden attributes for the model.
      *
-     * @param array $hidden
-     *
+     * @param  array  $hidden
      * @return $this
      */
     public function setHidden(array $hidden)
@@ -52,8 +53,7 @@ trait HidesAttributes
     /**
      * Add hidden attributes for the model.
      *
-     * @param array|string|null $attributes
-     *
+     * @param  array|string|null  $attributes
      * @return void
      */
     public function addHidden($attributes = null)
@@ -79,8 +79,7 @@ trait HidesAttributes
     /**
      * Set the visible attributes for the model.
      *
-     * @param array $visible
-     *
+     * @param  array  $visible
      * @return $this
      */
     public function setVisible(array $visible)
@@ -93,8 +92,7 @@ trait HidesAttributes
     /**
      * Add visible attributes for the model.
      *
-     * @param array|string|null $attributes
-     *
+     * @param  array|string|null  $attributes
      * @return void
      */
     public function addVisible($attributes = null)
@@ -108,8 +106,7 @@ trait HidesAttributes
     /**
      * Make the given, typically hidden, attributes visible.
      *
-     * @param array|string $attributes
-     *
+     * @param  array|string  $attributes
      * @return $this
      */
     public function makeVisible($attributes)
@@ -126,8 +123,7 @@ trait HidesAttributes
     /**
      * Make the given, typically visible, attributes hidden.
      *
-     * @param array|string $attributes
-     *
+     * @param  array|string  $attributes
      * @return $this
      */
     public function makeHidden($attributes)

+ 47 - 0
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesAndRestoresPropertyValues.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace LdapRecord\Models\Concerns;
+
+/** @mixin HasAttributes */
+trait SerializesAndRestoresPropertyValues
+{
+    /**
+     * Get the property value prepared for serialization.
+     *
+     * @param  string  $property
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function getSerializedPropertyValue($property, $value)
+    {
+        if ($property === 'original') {
+            return $this->originalToArray();
+        }
+
+        if ($property === 'attributes') {
+            return $this->attributesToArray();
+        }
+
+        return $value;
+    }
+
+    /**
+     * Get the unserialized property value after deserialization.
+     *
+     * @param  string  $property
+     * @param  mixed  $value
+     * @return mixed
+     */
+    protected function getUnserializedPropertyValue($property, $value)
+    {
+        if ($property === 'original') {
+            return $this->arrayToOriginal($value);
+        }
+
+        if ($property === 'attributes') {
+            return $this->arrayToAttributes($value);
+        }
+
+        return $value;
+    }
+}

+ 143 - 0
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Concerns/SerializesProperties.php

@@ -0,0 +1,143 @@
+<?php
+
+namespace LdapRecord\Models\Concerns;
+
+use ReflectionClass;
+use ReflectionProperty;
+
+trait SerializesProperties
+{
+    use SerializesAndRestoresPropertyValues;
+
+    /**
+     * Prepare the attributes for serialization.
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        $properties = (new ReflectionClass($this))->getProperties();
+
+        foreach ($properties as $property) {
+            $property->setValue($this, $this->getSerializedPropertyValue(
+                $property->getName(),
+                $this->getPropertyValue($property)
+            ));
+        }
+
+        return array_values(array_filter(array_map(function ($p) {
+            return $p->isStatic() ? null : $p->getName();
+        }, $properties)));
+    }
+
+    /**
+     * Restore the attributes after serialization.
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        foreach ((new ReflectionClass($this))->getProperties() as $property) {
+            if ($property->isStatic()) {
+                continue;
+            }
+
+            $property->setValue($this, $this->getUnserializedPropertyValue(
+                $property->getName(),
+                $this->getPropertyValue($property)
+            ));
+        }
+    }
+
+    /**
+     * Prepare the model for serialization.
+     *
+     * @return array
+     */
+    public function __serialize()
+    {
+        $values = [];
+
+        $properties = (new ReflectionClass($this))->getProperties();
+
+        $class = get_class($this);
+
+        foreach ($properties as $property) {
+            if ($property->isStatic()) {
+                continue;
+            }
+
+            $property->setAccessible(true);
+
+            if (! $property->isInitialized($this)) {
+                continue;
+            }
+
+            $name = $property->getName();
+
+            if ($property->isPrivate()) {
+                $name = "\0{$class}\0{$name}";
+            } elseif ($property->isProtected()) {
+                $name = "\0*\0{$name}";
+            }
+
+            $values[$name] = $this->getSerializedPropertyValue(
+                $property->getName(),
+                $this->getPropertyValue($property)
+            );
+        }
+
+        return $values;
+    }
+
+    /**
+     * Restore the model after serialization.
+     *
+     * @param  array  $values
+     * @return void
+     */
+    public function __unserialize(array $values)
+    {
+        $properties = (new ReflectionClass($this))->getProperties();
+
+        $class = get_class($this);
+
+        foreach ($properties as $property) {
+            if ($property->isStatic()) {
+                continue;
+            }
+
+            $name = $property->getName();
+
+            if ($property->isPrivate()) {
+                $name = "\0{$class}\0{$name}";
+            } elseif ($property->isProtected()) {
+                $name = "\0*\0{$name}";
+            }
+
+            if (! array_key_exists($name, $values)) {
+                continue;
+            }
+
+            $property->setAccessible(true);
+
+            $property->setValue(
+                $this,
+                $this->getUnserializedPropertyValue($property->getName(), $values[$name])
+            );
+        }
+    }
+
+    /**
+     * Get the property value for the given property.
+     *
+     * @param  ReflectionProperty  $property
+     * @return mixed
+     */
+    protected function getPropertyValue(ReflectionProperty $property)
+    {
+        $property->setAccessible(true);
+
+        return $property->getValue($this);
+    }
+}

+ 1 - 2
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DetectsResetIntegers.php

@@ -11,8 +11,7 @@ trait DetectsResetIntegers
      * LDAP attributes to instruct the server to reset the
      * value to an 'unset' or 'cleared' state.
      *
-     * @param mixed $value
-     *
+     * @param  mixed  $value
      * @return bool
      */
     protected function valueIsResetInteger($value)

+ 2 - 1
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/DirectoryServer/Entry.php

@@ -3,8 +3,9 @@
 namespace LdapRecord\Models\DirectoryServer;
 
 use LdapRecord\Models\Model;
+use LdapRecord\Models\Types\DirectoryServer;
 
-class Entry extends Model
+class Entry extends Model implements DirectoryServer
 {
     /**
      * The attribute key that contains the models object GUID.

+ 1 - 1
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Event.php

@@ -16,7 +16,7 @@ abstract class Event
     /**
      * Constructor.
      *
-     * @param Model $model
+     * @param  Model  $model
      */
     public function __construct(Model $model)
     {

+ 3 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Events/Renaming.php

@@ -23,9 +23,9 @@ class Renaming extends Event
     /**
      * Constructor.
      *
-     * @param Model  $model
-     * @param string $rdn
-     * @param string $newParentDn
+     * @param  Model  $model
+     * @param  string  $rdn
+     * @param  string  $newParentDn
      */
     public function __construct(Model $model, $rdn, $newParentDn)
     {

+ 1 - 2
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Entry.php

@@ -44,8 +44,7 @@ class Entry extends BaseEntry implements FreeIPA
     /**
      * Create a new query builder.
      *
-     * @param Connection $connection
-     *
+     * @param  Connection  $connection
      * @return FreeIpaBuilder
      */
     public function newQueryBuilder(Connection $connection)

+ 2 - 3
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/FreeIPA/Scopes/AddEntryUuidToSelects.php

@@ -11,9 +11,8 @@ class AddEntryUuidToSelects implements Scope
     /**
      * Add the entry UUID to the selected attributes.
      *
-     * @param Builder $query
-     * @param Model   $model
-     *
+     * @param  Builder  $query
+     * @param  Model  $model
      * @return void
      */
     public function apply(Builder $query, Model $model)

文件差異過大導致無法顯示
+ 216 - 175
data/web/inc/lib/vendor/directorytree/ldaprecord/src/Models/Model.php


部分文件因文件數量過多而無法顯示