Forráskód Böngészése

add LDAP framework

causefx 8 éve
szülő
commit
dfd9290423
100 módosított fájl, 17828 hozzáadás és 2 törlés
  1. 2 1
      api/composer.json
  2. 498 1
      api/composer.lock
  3. 2 0
      api/vendor/adldap2/adldap2/.gitattributes
  4. 8 0
      api/vendor/adldap2/adldap2/.github/issue_template.md
  5. 1 0
      api/vendor/adldap2/adldap2/.gitignore
  6. 5 0
      api/vendor/adldap2/adldap2/.scrutinizer.yml
  7. 24 0
      api/vendor/adldap2/adldap2/.travis.yml
  8. 44 0
      api/vendor/adldap2/adldap2/changelog.md
  9. 48 0
      api/vendor/adldap2/adldap2/composer.json
  10. 79 0
      api/vendor/adldap2/adldap2/docs/authenticating.md
  11. 151 0
      api/vendor/adldap2/adldap2/docs/configuration.md
  12. 143 0
      api/vendor/adldap2/adldap2/docs/connecting.md
  13. 117 0
      api/vendor/adldap2/adldap2/docs/distinguished-names.md
  14. 32 0
      api/vendor/adldap2/adldap2/docs/models/computer.md
  15. 13 0
      api/vendor/adldap2/adldap2/docs/models/contact.md
  16. 24 0
      api/vendor/adldap2/adldap2/docs/models/container.md
  17. 6 0
      api/vendor/adldap2/adldap2/docs/models/entry.md
  18. 203 0
      api/vendor/adldap2/adldap2/docs/models/group.md
  19. 625 0
      api/vendor/adldap2/adldap2/docs/models/model.md
  20. 27 0
      api/vendor/adldap2/adldap2/docs/models/ou.md
  21. 50 0
      api/vendor/adldap2/adldap2/docs/models/printer.md
  22. 33 0
      api/vendor/adldap2/adldap2/docs/models/root-dse.md
  23. 13 0
      api/vendor/adldap2/adldap2/docs/models/traits/has-critical-system-object.md
  24. 11 0
      api/vendor/adldap2/adldap2/docs/models/traits/has-description.md
  25. 2 0
      api/vendor/adldap2/adldap2/docs/models/traits/has-last-login-last-logoff.md
  26. 166 0
      api/vendor/adldap2/adldap2/docs/models/traits/has-member-of.md
  27. 138 0
      api/vendor/adldap2/adldap2/docs/models/user.md
  28. 640 0
      api/vendor/adldap2/adldap2/docs/query-builder.md
  29. 69 0
      api/vendor/adldap2/adldap2/docs/quick-start.md
  30. 53 0
      api/vendor/adldap2/adldap2/docs/schema.md
  31. 79 0
      api/vendor/adldap2/adldap2/docs/troubleshooting.md
  32. 8 0
      api/vendor/adldap2/adldap2/license.md
  33. 18 0
      api/vendor/adldap2/adldap2/phpunit.xml
  34. 89 0
      api/vendor/adldap2/adldap2/readme.md
  35. 147 0
      api/vendor/adldap2/adldap2/src/Adldap.php
  36. 8 0
      api/vendor/adldap2/adldap2/src/AdldapException.php
  37. 93 0
      api/vendor/adldap2/adldap2/src/AdldapInterface.php
  38. 17 0
      api/vendor/adldap2/adldap2/src/Auth/BindException.php
  39. 145 0
      api/vendor/adldap2/adldap2/src/Auth/Guard.php
  40. 56 0
      api/vendor/adldap2/adldap2/src/Auth/GuardInterface.php
  41. 10 0
      api/vendor/adldap2/adldap2/src/Auth/PasswordRequiredException.php
  42. 10 0
      api/vendor/adldap2/adldap2/src/Auth/UsernameRequiredException.php
  43. 18 0
      api/vendor/adldap2/adldap2/src/Configuration/ConfigurationException.php
  44. 167 0
      api/vendor/adldap2/adldap2/src/Configuration/DomainConfiguration.php
  45. 27 0
      api/vendor/adldap2/adldap2/src/Configuration/Validators/ArrayValidator.php
  46. 27 0
      api/vendor/adldap2/adldap2/src/Configuration/Validators/BooleanValidator.php
  47. 27 0
      api/vendor/adldap2/adldap2/src/Configuration/Validators/IntegerValidator.php
  48. 27 0
      api/vendor/adldap2/adldap2/src/Configuration/Validators/StringOrNullValidator.php
  49. 48 0
      api/vendor/adldap2/adldap2/src/Configuration/Validators/Validator.php
  50. 10 0
      api/vendor/adldap2/adldap2/src/Connections/ConnectionException.php
  51. 481 0
      api/vendor/adldap2/adldap2/src/Connections/ConnectionInterface.php
  52. 456 0
      api/vendor/adldap2/adldap2/src/Connections/Ldap.php
  53. 52 0
      api/vendor/adldap2/adldap2/src/Connections/LdapFunctionSupportTrait.php
  54. 260 0
      api/vendor/adldap2/adldap2/src/Connections/Provider.php
  55. 127 0
      api/vendor/adldap2/adldap2/src/Connections/ProviderInterface.php
  56. 412 0
      api/vendor/adldap2/adldap2/src/Models/Attributes/AccountControl.php
  57. 371 0
      api/vendor/adldap2/adldap2/src/Models/Attributes/DistinguishedName.php
  58. 154 0
      api/vendor/adldap2/adldap2/src/Models/Attributes/Guid.php
  59. 101 0
      api/vendor/adldap2/adldap2/src/Models/Attributes/Sid.php
  60. 245 0
      api/vendor/adldap2/adldap2/src/Models/BatchModification.php
  61. 93 0
      api/vendor/adldap2/adldap2/src/Models/Computer.php
  62. 319 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasAttributes.php
  63. 18 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasCriticalSystemObject.php
  64. 30 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasDescription.php
  65. 42 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasLastLogonAndLogOff.php
  66. 240 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasMemberOf.php
  67. 50 0
      api/vendor/adldap2/adldap2/src/Models/Concerns/HasUserAccountControl.php
  68. 15 0
      api/vendor/adldap2/adldap2/src/Models/Contact.php
  69. 30 0
      api/vendor/adldap2/adldap2/src/Models/Container.php
  70. 15 0
      api/vendor/adldap2/adldap2/src/Models/Entry.php
  71. 192 0
      api/vendor/adldap2/adldap2/src/Models/Factory.php
  72. 269 0
      api/vendor/adldap2/adldap2/src/Models/Group.php
  73. 1066 0
      api/vendor/adldap2/adldap2/src/Models/Model.php
  74. 38 0
      api/vendor/adldap2/adldap2/src/Models/ModelDoesNotExistException.php
  75. 47 0
      api/vendor/adldap2/adldap2/src/Models/ModelNotFoundException.php
  76. 23 0
      api/vendor/adldap2/adldap2/src/Models/OrganizationalUnit.php
  77. 288 0
      api/vendor/adldap2/adldap2/src/Models/Printer.php
  78. 90 0
      api/vendor/adldap2/adldap2/src/Models/RootDse.php
  79. 1152 0
      api/vendor/adldap2/adldap2/src/Models/User.php
  80. 18 0
      api/vendor/adldap2/adldap2/src/Models/UserPasswordIncorrectException.php
  81. 18 0
      api/vendor/adldap2/adldap2/src/Models/UserPasswordPolicyException.php
  82. 1510 0
      api/vendor/adldap2/adldap2/src/Query/Builder.php
  83. 267 0
      api/vendor/adldap2/adldap2/src/Query/Factory.php
  84. 390 0
      api/vendor/adldap2/adldap2/src/Query/Grammar.php
  85. 117 0
      api/vendor/adldap2/adldap2/src/Query/Operator.php
  86. 206 0
      api/vendor/adldap2/adldap2/src/Query/Paginator.php
  87. 222 0
      api/vendor/adldap2/adldap2/src/Query/Processor.php
  88. 1223 0
      api/vendor/adldap2/adldap2/src/Schemas/ActiveDirectory.php
  89. 30 0
      api/vendor/adldap2/adldap2/src/Schemas/FreeIPA.php
  90. 94 0
      api/vendor/adldap2/adldap2/src/Schemas/OpenLDAP.php
  91. 1323 0
      api/vendor/adldap2/adldap2/src/Schemas/SchemaInterface.php
  92. 344 0
      api/vendor/adldap2/adldap2/src/Utilities.php
  93. 2 0
      api/vendor/composer/autoload_files.php
  94. 9 0
      api/vendor/composer/autoload_psr4.php
  95. 59 0
      api/vendor/composer/autoload_static.php
  96. 515 0
      api/vendor/composer/installed.json
  97. 19 0
      api/vendor/doctrine/inflector/LICENSE
  98. 6 0
      api/vendor/doctrine/inflector/README.md
  99. 32 0
      api/vendor/doctrine/inflector/composer.json
  100. 490 0
      api/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php

+ 2 - 1
api/composer.json

@@ -4,6 +4,7 @@
         "lcobucci/jwt": "^3.2",
         "composer/semver": "^1.4",
         "phpmailer/phpmailer": "^6.0",
-        "rmccue/requests": "^1.7"
+        "rmccue/requests": "^1.7",
+        "adldap2/adldap2": "^8.0"
     }
 }

+ 498 - 1
api/composer.lock

@@ -4,8 +4,60 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "b23a4e199a4e93b84375b6091c164a53",
+    "content-hash": "a92166d039956c149b8f58c54111358c",
     "packages": [
+        {
+            "name": "adldap2/adldap2",
+            "version": "v8.0.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Adldap2/Adldap2.git",
+                "reference": "5ab7df6f24bf952666e926ce5220a21325f8f277"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/5ab7df6f24bf952666e926ce5220a21325f8f277",
+                "reference": "5ab7df6f24bf952666e926ce5220a21325f8f277",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ldap": "*",
+                "illuminate/support": "~5.0",
+                "php": ">=5.5.9"
+            },
+            "require-dev": {
+                "mockery/mockery": "~0.9|~1.0",
+                "phpunit/phpunit": "~4.8|~5.6"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Adldap\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Steve Bauman",
+                    "email": "steven_bauman@outlook.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A PHP LDAP Package for humans.",
+            "keywords": [
+                "active directory",
+                "ad",
+                "adLDAP",
+                "adldap2",
+                "directory",
+                "ldap",
+                "windows"
+            ],
+            "time": "2018-01-02T14:14:44+00:00"
+        },
         {
             "name": "composer/semver",
             "version": "1.4.2",
@@ -135,6 +187,174 @@
             ],
             "time": "2017-09-25T15:57:54+00:00"
         },
+        {
+            "name": "doctrine/inflector",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
+                "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Common String Manipulations with regard to casing and singular/plural rules.",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "inflection",
+                "pluralize",
+                "singularize",
+                "string"
+            ],
+            "time": "2018-01-09T20:05:19+00:00"
+        },
+        {
+            "name": "illuminate/contracts",
+            "version": "v5.5.28",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/contracts.git",
+                "reference": "03e9014d2091a30b025c895aa6d39c2755576ea5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/03e9014d2091a30b025c895aa6d39c2755576ea5",
+                "reference": "03e9014d2091a30b025c895aa6d39c2755576ea5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0",
+                "psr/container": "~1.0",
+                "psr/simple-cache": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Contracts\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Contracts package.",
+            "homepage": "https://laravel.com",
+            "time": "2017-11-22T19:01:14+00:00"
+        },
+        {
+            "name": "illuminate/support",
+            "version": "v5.5.28",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/illuminate/support.git",
+                "reference": "4db3cc82b483172b1b25d9dfcec684927f5c8cf9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/4db3cc82b483172b1b25d9dfcec684927f5c8cf9",
+                "reference": "4db3cc82b483172b1b25d9dfcec684927f5c8cf9",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/inflector": "~1.1",
+                "ext-mbstring": "*",
+                "illuminate/contracts": "5.5.*",
+                "nesbot/carbon": "^1.20",
+                "php": ">=7.0"
+            },
+            "replace": {
+                "tightenco/collect": "self.version"
+            },
+            "suggest": {
+                "illuminate/filesystem": "Required to use the composer class (5.2.*).",
+                "symfony/process": "Required to use the composer class (~3.3).",
+                "symfony/var-dumper": "Required to use the dd function (~3.3)."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Illuminate\\Support\\": ""
+                },
+                "files": [
+                    "helpers.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Taylor Otwell",
+                    "email": "taylor@laravel.com"
+                }
+            ],
+            "description": "The Illuminate Support package.",
+            "homepage": "https://laravel.com",
+            "time": "2017-12-24T20:02:59+00:00"
+        },
         {
             "name": "lcobucci/jwt",
             "version": "3.2.2",
@@ -193,6 +413,59 @@
             ],
             "time": "2017-09-01T08:23:26+00:00"
         },
+        {
+            "name": "nesbot/carbon",
+            "version": "1.22.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/briannesbitt/Carbon.git",
+                "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+                "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "symfony/translation": "~2.6 || ~3.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "~2",
+                "phpunit/phpunit": "~4.0 || ~5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.23-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Carbon\\": "src/Carbon/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Brian Nesbitt",
+                    "email": "brian@nesbot.com",
+                    "homepage": "http://nesbot.com"
+                }
+            ],
+            "description": "A simple API extension for DateTime.",
+            "homepage": "http://carbon.nesbot.com",
+            "keywords": [
+                "date",
+                "datetime",
+                "time"
+            ],
+            "time": "2017-01-16T07:55:07+00:00"
+        },
         {
             "name": "phpmailer/phpmailer",
             "version": "v6.0.3",
@@ -259,6 +532,103 @@
             "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
             "time": "2018-01-05T13:19:58+00:00"
         },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24",
+                "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "time": "2017-01-02T13:31:39+00:00"
+        },
         {
             "name": "rmccue/requests",
             "version": "v1.7.0",
@@ -307,6 +677,133 @@
                 "sockets"
             ],
             "time": "2016-10-13T00:11:37+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.6.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+                "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2017-10-11T12:05:26+00:00"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v3.4.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a",
+                "reference": "17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/config": "<2.8",
+                "symfony/dependency-injection": "<3.4",
+                "symfony/yaml": "<3.4"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/finder": "~2.8|~3.0|~4.0",
+                "symfony/intl": "^2.8.18|^3.2.5|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "psr/log": "To use logging capability in translator",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Translation Component",
+            "homepage": "https://symfony.com",
+            "time": "2018-01-03T07:37:34+00:00"
         }
     ],
     "packages-dev": [],

+ 2 - 0
api/vendor/adldap2/adldap2/.gitattributes

@@ -0,0 +1,2 @@
+examples/ export-ignore
+tests/ export-ignore

+ 8 - 0
api/vendor/adldap2/adldap2/.github/issue_template.md

@@ -0,0 +1,8 @@
+- Adldap2 Version: #.#
+- LDAP Type: <!-- Active Directory or OpenLDAP? -->
+- PHP Version: #.#
+
+### Description:
+
+
+### Steps To Reproduce:

+ 1 - 0
api/vendor/adldap2/adldap2/.gitignore

@@ -0,0 +1 @@
+/vendor

+ 5 - 0
api/vendor/adldap2/adldap2/.scrutinizer.yml

@@ -0,0 +1,5 @@
+filter:
+    excluded_paths:
+        - examples/*
+        - tests/*
+        - src/Schemas/*

+ 24 - 0
api/vendor/adldap2/adldap2/.travis.yml

@@ -0,0 +1,24 @@
+language: php
+
+php:
+  - 5.5
+  - 5.6
+  - 7.0
+  - 7.1
+  - 7.2
+
+before_install: echo "extension=ldap.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+
+before_script:
+  - travis_retry composer self-update
+  - travis_retry composer install --prefer-source --no-interaction
+
+script: ./vendor/bin/phpunit
+
+branches:
+  only:
+    - master
+    - v5.2
+    - v6.0
+    - v6.1
+    - v7.0

+ 44 - 0
api/vendor/adldap2/adldap2/changelog.md

@@ -0,0 +1,44 @@
+# Unreleased
+
+No changes.
+
+## v8.0.0
+
+### Added
+
+- Added configurable models via the Schema ([caf1750](https://github.com/Adldap2/Adldap2/commit/caf17505f6eac609e028cc7763da468c9c59ca6a))
+- Added `isValidGuid()` method on `Utilities` ([562776a](https://github.com/Adldap2/Adldap2/commit/562776a4a0a63fcb52c9d963bc52e91bbe70c9b2))
+- Added MemberOf Recursive filter to Schema ([5846270](https://github.com/Adldap2/Adldap2/commit/584627088893a221ec43a785d1aa00f5a367a50d))
+- Added `notFilter()` method to query `Builder` ([#418](https://github.com/Adldap2/Adldap2/issues/418))-([8f4d969](https://github.com/Adldap2/Adldap2/commit/8f4d9698afda27d99dbf0dca0c4643f367dbd7d4)) 
+- Added `homeDrive` and `homeDirectory` methods to `Schema` ([8966491](https://github.com/Adldap2/Adldap2/commit/8966491507376430409d0847160450ce595323cd),[b1fe3f9](https://github.com/Adldap2/Adldap2/commit/b1fe3f9e2bbedf0fc9113d62e707808153d7b2f2))
+- Added `isValid()` method to `BatchModification` ([67db6d9](https://github.com/Adldap2/Adldap2/commit/67db6d973b6b5147e4391fac4f6be024e97e2753))
+- Added `KEY_ATTRIB`, `KEY_MODTYPE` & `KEY_VALUES` constants to `BatchModification` ([1f29859](https://github.com/Adldap2/Adldap2/commit/1f2985912df61d7a11e5f196ecbcc1f460383758))
+- Added `lockoutTime()`, `filterEnabled()` and `filterDisabled()` methods to `OpenLDAP` Schema ([96e0e1f](https://github.com/Adldap2/Adldap2/commit/96e0e1fd8298bcfdefb002d71abf4a4fb06b83a6))
+- Added `Guid` and `Sid` attribute classes for converting GUIDs and SIDs to string & binary ([03f7074](https://github.com/Adldap2/Adldap2/commit/03f7074d56af95ad69a17f2f77ee238c708a1841))
+- Added `inOu()` method to `Model` to check if a `Model` instance is inside a given OU ([fdc6b17](https://github.com/Adldap2/Adldap2/commit/fdc6b177c630993cb541a72ae4f79d4cb581e459))
+- Added `newBatchModification()` method to `Model` ([35b3f70](https://github.com/Adldap2/Adldap2/commit/35b3f70686bc23d72e94ecf5e6c349404b926097))
+
+### Changed
+
+- GUIDs are converted to hex before searching if the Schema requires it ([de40105](https://github.com/Adldap2/Adldap2/commit/de401055abf1d4311f087ac1bae2ed0048fcdb75))
+- GUIDs / SIDs are converted to strings only if the Schema requires it ([d89f105](https://github.com/Adldap2/Adldap2/commit/d89f105f0335fd77b48f467449aa559c4e9169af))
+- An array can now be used in the `find()` method to search for multiple records ([06a25ee](https://github.com/Adldap2/Adldap2/commit/06a25ee5501aebf457c42099788a8cf3293b2e39))
+- Rebinds as the administrator are now properly re-bound ([ac5e73b](https://github.com/Adldap2/Adldap2/commit/ac5e73bfee16bba83a16e73523c39935f390c4a3))
+- Account prefix and suffix are now only applied if the given prefix and suffix are null ([#406](https://github.com/Adldap2/Adldap2/issues/406))-([7db8896](https://github.com/Adldap2/Adldap2/commit/7db8896ea69516a258bd07f5d8cea50683bc1da5))
+- GUIDs and SIDs are now converted to their string equivalents upon serialization ([f43d4dc](https://github.com/Adldap2/Adldap2/commit/f43d4dc504b06013b549144f28f1b9d791610b38))
+- Exception is always thrown when a batch modification is invalid ([b45bfef](https://github.com/Adldap2/Adldap2/commit/b45bfeff4c4e3ae9b91e6499ff0c88d675923a03))
+- `Utilities::littleEndian()` static method has been removed ([57360b1](https://github.com/Adldap2/Adldap2/commit/57360b10dcf57ae013a3688cb168e637b58ba587))
+- GUIDs are now used as the authentication identifier for Laravel ([9033159](https://github.com/Adldap2/Adldap2/commit/90331598169de6ec5446917fefb6334fa72e4a47))
+- Removed `__destruct()` from the `ProviderInterface` ([a951f29](https://github.com/Adldap2/Adldap2/commit/a951f29ceefeb47431e2147f1251f1383cdd3bc9))
+- `DistinguishedName` properties are now public ([9d18d7d](https://github.com/Adldap2/Adldap2/commit/9d18d7d96ac9f6c00770572c1c2ae4fe2f49aa06))
+- Model traits are now contained in the `Concerns` namespace ([81759a5](https://github.com/Adldap2/Adldap2/commit/81759a5e3b32cf5c9c4fa9658ddf84a19f0daa9e))
+- Moved attribute methods / properties into `Concerns` trait ([5be9ff4](https://github.com/Adldap2/Adldap2/commit/5be9ff439cc6f3f1cdb6d85e15c94d109747b437))
+- Moved `Paginator` into the `Query` namespace ([33f9048](https://github.com/Adldap2/Adldap2/commit/33f9048b77f811a64a3d8d6362589ba8b2348b3a))
+- Moved `AccountControl` into `Models\Attributes` namespace ([58482c4](https://github.com/Adldap2/Adldap2/commit/58482c48fcab16e772953f1384ed86b52bc76e4c))
+- Moved `DistinguishedName` into `Models\Attributes` namespace ([62c84c1](https://github.com/Adldap2/Adldap2/commit/62c84c1e3a1fc63a960c7f5122b3c6e0a5487b4b))
+- Moved `BatchModification` into `Models` namespace ([405bfdc](https://github.com/Adldap2/Adldap2/commit/405bfdca57b4ace6e224bf9c0aae7187cbe67e9d))
+- Moved `Search\Factory` into `Query` namespace ([ae7d029](https://github.com/Adldap2/Adldap2/commit/ae7d029d7ca4c236726c40e24c21b3794b77e7ae))
+
+## Fixed
+
+- Models are now serialized properly ([#430](https://github.com/Adldap2/Adldap2/issues/430))-([d207376](https://github.com/Adldap2/Adldap2/commit/d207376a004a5a83af33dc7584237fd6b5c57d6a))

+ 48 - 0
api/vendor/adldap2/adldap2/composer.json

@@ -0,0 +1,48 @@
+{
+    "name": "adldap2/adldap2",
+    "type": "library",
+    "description": "A PHP LDAP Package for humans.",
+    "keywords": [
+        "active directory",
+        "directory",
+        "ad",
+        "ldap",
+        "windows",
+        "adldap",
+        "adldap2"
+    ],
+    "license": "MIT",
+    "support": {
+        "issues": "https://github.com/Adldap2/Adldap2/issues",
+        "source": "https://github.com/Adldap2/Adldap2"
+    },
+    "authors": [
+        {
+            "name": "Steve Bauman",
+            "email": "steven_bauman@outlook.com",
+            "role": "Developer"
+        }
+    ],
+    "require": {
+        "php": ">=5.5.9",
+        "illuminate/support": "~5.0",
+        "ext-ldap": "*"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "~4.8|~5.6",
+        "mockery/mockery": "~0.9|~1.0"
+    },
+    "archive": {
+        "exclude": ["/examples", "/tests"]
+    },
+    "autoload": {
+        "psr-4": {
+            "Adldap\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Adldap\\Tests\\": "tests/"
+        }
+    }
+}

+ 79 - 0
api/vendor/adldap2/adldap2/docs/authenticating.md

@@ -0,0 +1,79 @@
+# Authenticating
+
+To authenticate users using your LDAP server, call the `auth()->attempt()`
+method on your provider:
+
+```php
+try {
+
+    if ($provider->auth()->attempt($username, $password)) {
+        // Credentials were correct.
+    } else {
+        // Credentials were incorrect.
+    }
+
+} catch (\Adldap\Auth\UsernameRequiredException $e) {
+    // The user didn't supply a username.
+} catch (\Adldap\Auth\PasswordRequiredException $e) {
+    // The user didn't supply a password.
+}
+```
+
+> **Note**: Authenticating does not actually set up a PHP session or perform any
+> sort of login functionality. The attempt() method merely tries to bind to
+> your LDAP server as the specified user and returns true / false on its result.
+
+## Binding as Authenticated Users
+
+To bind the users to your LDAP connection that you authenticate (which
+means *run all further LDAP operations under this user*),
+pass in `true` into the third parameter:
+
+```php
+$username = 'jdoe';
+$password = 'Password123';
+
+if ($provider->auth()->attempt($username, $password, $bindAsUser = true)) {
+    // Credentials were correct.
+    
+    // All LDAP operations will be ran under John Doe.
+}
+```
+
+> **Note**: By default, `$bindAsUser` is false, this means that all LDAP
+> operations are ran under your configured administrator account unless
+> otherwise specified.
+
+## Manually Binding as Administrator
+
+To manually bind as your configured administrator, use the `bindAsAdministrator()` method:
+
+```php
+try {
+    $provider->auth()->bindAsAdministrator();
+
+    // Successfully bound to server.
+} catch (\Adldap\Auth\BindException $e) {
+    // There was an issue binding to the LDAP server.
+}
+```
+
+## Manually Binding as A User
+
+To manually bind as a user, use the `bind()` method:
+
+```php
+try {
+    $provider->auth()->bind($username, $password);
+
+     // Successfully bound to server.
+} catch (\Adldap\Auth\BindException $e) {
+    // There was an issue binding to the LDAP server.
+}
+```
+
+> **Note**: Manually binding as a user **will not** validate their
+> username or password to ensure they are not empty.
+>
+> This means, a user could pass in empty strings and could anonymously
+> authenticate to your server if you don't validate their input.

+ 151 - 0
api/vendor/adldap2/adldap2/docs/configuration.md

@@ -0,0 +1,151 @@
+# Configuration
+
+- [Using an Array](#using-a-configuration-array)
+- [Using DomainConfiguration](#using-a-domainconfiguration-object)
+- [Definitions](#definitions)
+    - [Account Prefix (optional)](#account-prefix-optional)
+    - [Account Suffix (optional)](#account-suffix-optional)
+    - [Admin Account Suffix (optional)](#admin-account-suffix-optional)
+    - [Domain Controllers (required)](#domain-controllers-required)
+    - [Port (optional)](#port-optional)
+    - [Base Distinguished Name (required)](#base-distinguished-name-required)
+    - [Administrator Username & Password (required)](#administrator-username--password-required)
+    - [Follow Referrals (optional)](#follow-referrals-optional)
+    - [SSL & TLS (optional)](#ssl--tls-optional)
+    - [Timeout](#timeout)
+    - [Custom Options](#custom-options)
+
+Configuring Adldap2 is really easy. Let's get started.
+
+## Using a configuration array
+
+You can configure Adldap2 by supplying an array. Keep in mind not all of these are required. This will be discussed below.
+Here is an example array with all possible configuration options:
+
+```php
+// Create the configuration array.
+$config = [    // Mandatory Configuration Options
+    'domain_controllers'    => ['corp-dc1.corp.acme.org', 'corp-dc2.corp.acme.org'],
+    'base_dn'               => 'dc=corp,dc=acme,dc=org',
+    'admin_username'        => 'admin',
+    'admin_password'        => 'password',
+    
+    // Optional Configuration Options
+    'account_prefix'        => 'ACME-',
+    'account_suffix'        => '@acme.org',
+    'admin_account_prefix'  => 'ACME-ADMIN-',
+    'admin_account_suffix'  => '@acme.org',
+    'port'                  => 389,
+    'follow_referrals'      => false,
+    'use_ssl'               => false,
+    'use_tls'               => false,
+    'timeout'               => 5,
+    
+    // Custom LDAP Options
+    'custom_options'        => [
+        // See: http://php.net/ldap_set_option
+        LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
+    ]
+];
+
+// Create a new Adldap Provider instance.
+$provider = new \Adldap\Connections\Provider($config);
+```
+
+## Using a DomainConfiguration object
+
+If you'd prefer, you can also construct a `DomainConfiguration` object:
+
+```php
+// Setting configuration options via construct:
+$config = new \Adldap\Configuration\DomainConfiguration([
+    'domain_controllers' => [
+        'corp-dc1.corp.acme.org',
+        'corp-dc2.corp.acme.org',
+    ],
+]);
+
+// Setting configuration options via `set()` method:
+$config->set('domain_controllers', [
+    'corp-dc1.corp.acme.org',
+    'corp-dc2.corp.acme.org',
+]);
+
+$provider = new \Adldap\Connections\Provider($config);
+```
+
+## Definitions
+
+### Account Prefix (optional)
+
+The account prefix option is the prefix of your user accounts in AD. This is usually not needed (if utilizing the
+account suffix), however the functionality is in place if you would like to only allow certain users with
+the specified prefix to login, or add a domain so you're users do not have to specify one.
+
+### Account Suffix (optional)
+
+The account suffix option is the suffix of your user accounts in AD. For example, if your domain DN is `DC=corp,DC=acme,DC=org`,
+then your account suffix would be `@corp.acme.org`. This is then appended to then end of your user accounts on authentication.
+
+For example, if you're binding as a user, and your username is `jdoe`, then Adldap would try to authenticate with
+your server as `jdoe@corp.acme.org`.
+
+### Admin Account Prefix (optional)
+
+The admin account prefix option is the prefix of your administrator account in AD. Having a separate prefix for user accounts
+and administrator accounts allows you to bind your admin under a different prefix than user accounts.
+
+### Admin Account Suffix (optional)
+
+The admin account suffix option is the suffix of your administrator account in AD. Having a separate suffix for user accounts
+and administrator accounts allows you to bind your admin under a different suffix than user accounts.
+
+### Domain Controllers (required)
+
+The domain controllers option is an array of servers located on your network that serve Active Directory. You insert as many
+servers or as little as you'd like depending on your forest (with the minimum of one of course).
+
+For example, if the server name that hosts AD on my network is named `ACME-DC01`, then I would insert `['ACME-DC01.corp.acme.org']`
+inside the domain controllers option array.
+
+### Port (optional)
+
+The port option is used for authenticating and binding to your AD server. The default ports are already used for non SSL and SSL connections (389 and 636).
+
+Only insert a port if your AD server uses a unique port.
+
+### Base Distinguished Name (required)
+
+The base distinguished name is the base distinguished name you'd like to perform operations on. An example base DN would be `DC=corp,DC=acme,DC=org`.
+
+If one is not defined, you will not retrieve any search results.
+
+### Administrator Username & Password (required)
+
+When connecting to your AD server, an administrator username and password is required to be able to query and run operations on your server(s).
+You can use any account that has these permissions.
+
+### Follow Referrals (optional)
+
+The follow referrals option is a boolean to tell active directory to follow a referral to another server on your network if the
+server queried knows the information your asking for exists, but does not yet contain a copy of it locally. This option is defaulted to false.
+
+For more information, visit: https://technet.microsoft.com/en-us/library/cc978014.aspx
+
+### SSL & TLS (optional)
+
+If you need to be able to change user passwords on your server, then an SSL *or* TLS connection is required. All other operations
+are allowed on unsecured protocols. These options are definitely recommended if you have the ability to connect to your server
+securely.
+
+### Timeout
+
+The timeout option allows you to configure the amount of seconds to wait until your application receives a response from your LDAP server.
+
+The default is 5 seconds.
+
+### Custom Options
+
+Arbitrary options can be set for the connection to fine-tune TLS and connection behavior. Please note that `LDAP_OPT_PROTOCOL_VERSION`,
+`LDAP_OPT_NETWORK_TIMEOUT` and `LDAP_OPT_REFERRALS` will be ignored if set. These are set above with the `version`, `timeout` and
+`follow_referrals` keys respectively. Valid options are listed in the [PHP documentation for ldap_set_option](http://php.net/ldap_set_option).

+ 143 - 0
api/vendor/adldap2/adldap2/docs/connecting.md

@@ -0,0 +1,143 @@
+# Connecting
+
+- [Setup](#setup)
+- [Dynamically Connecting](#dynamically-connecting)
+- [Setting a Default Connection](#setting-a-default-connection)
+- [Custom Connections](#custom-connections)
+- [Custom Schemas](#custom-schemas)
+
+## Setup
+
+After installation, you'll need to create a couple objects to start running operations on your LDAP server.
+
+First, we'll define our configuration array (outlined [here](configuration.md)):
+
+```php
+$config = [
+    'default' => ['...'],
+];
+```
+
+We'll then create a new Adldap instance and pass in the configuration array:
+
+```php
+$ad = new \Adldap\Adldap($config);
+
+try {
+    // Connect to the provider you specified in your configuration.
+    $provider = $ad->connect('default');
+    
+    // Connection was successful.
+    
+    // We can now perform operations on the connection.
+    $user = $provider->search()->users()->find('jdoe');
+
+} catch (\Adldap\Auth\BindException $e) {
+    die("Can't connect / bind to the LDAP server! Error: $e");
+}
+```
+
+## Dynamically Connecting
+
+If you prefer, you can actually call all provider methods on your default provider through your `Adldap` instance.
+
+For example:
+
+```php
+$ad = new \Adldap\Adldap($config);
+
+try {
+    $user = $ad->search()->users()->find('jdoe');
+} catch (\Adldap\Auth\BindException $e) {
+    //
+}
+```
+
+Adldap will automatically connect to your default provider and perform all method calls upon it.
+
+## Setting a default connection
+
+If you name your connection something other than `default`, you'll have to set that as your default connection:
+
+For example:
+
+```php
+$config = [
+    'ad_acme_company' => ['...'],
+];
+
+$ad = new \Adldap\Adldap($config);
+
+$ad->setDefaultProvider('ad_acme_company');
+
+$user = $ad->search()->users()->find('jdoe');
+```
+
+Adldap will automatically connect to your new default provider.
+
+## Custom Connections
+
+Whenever you don't supply a new provider with an object that's an instance of
+`Adldap\Connections\ConnectionInterface`, a default connection is created for you.
+
+A connection object is a wrapper for PHP's LDAP calls. This allows you to tweak how
+things are passed into these methods if needed.
+
+To create a custom connection, you can either extend the default connection class
+(`Adldap\Connections\Ldap`), or implement the `ConnectionInterface`.
+
+For example:
+
+```php
+class CustomConnection extends \Adldap\Connections\Ldap
+{
+    public function connect($hostname = [], $port = '389')
+    {
+        // Perform an `ldap_connect()` your own way...
+    }
+}
+```
+
+Now that we have our own connection class, we can instantiate it and pass it to the provider:
+
+```php
+$config = ['...'];
+
+$connection = new CustomConnection();
+
+$provider = new \Adldap\Connections\Provider($config, $connection);
+```
+
+## Custom Schemas
+
+Some LDAP installations differ and you may need to tweak what some attributes are. This is where the schema comes in.
+
+By default, if no schema is passed into the third parameter of a provider instance, a default schema is created.
+
+The schema must extend from an already existing schema, or implement `Adldap\Contracts\Schemas\SchemaInterface`.
+
+Let's create a custom schema:
+
+```php
+class CustomSchema extends \Adldap\Schemas\ActiveDirectory
+{
+    public function email()
+    {
+        return 'mail';
+    }
+}
+```
+
+Now we'll put it all together:
+
+```php
+$config = ['...'];
+
+$connection = new CustomConnection();
+
+$schema = new CustomSchema();
+
+$provider = new \Adldap\Connections\Provider($config, $connection, $schema);
+```
+
+Now our provider will utilize our custom schema and connection classes.

+ 117 - 0
api/vendor/adldap2/adldap2/docs/distinguished-names.md

@@ -0,0 +1,117 @@
+## Working With Distinguished Names
+
+Working with DN strings are a pain, but they're about to get easier. Adldap includes a DN builder for easily modifying and
+creating DN strings.
+
+> **Note**: All values inserted into DN methods are escaped. You don't need to escape **any** values before hand.
+
+#### Creating a New DN
+
+To create a new DN, construct a new `\Adldap\Models\Attributes\DistinguishedName` instance:
+
+```php
+$dn = new \Adldap\Models\Attributes\DistinguishedName();
+```
+    
+You can also pass in a current DN string and start modifying it:
+
+```php
+$currentDn = 'cn=John Doe,ou=Accounting,dc=corp,dc=acme,dc=org';
+
+$dn = new \Adldap\Models\Attributes\DistinguishedName($currentDn);
+```
+    
+#### Adding / Removing a Domain Component
+
+```php
+// Add Domain Component
+$dn->addDc('corp');
+
+// Remove Domain Component
+$dn->removeDc('corp');
+```
+
+#### Adding / Removing an Organizational Unit
+
+```php
+// Add Organizational Unit
+$dn->addOu('Accounting');
+    
+// Remove Organizational Unit
+$dn->removeOu('Accounting');
+```
+
+#### Adding / Removing Common Names
+
+```php
+// Add Common Name
+$dn->addCn('John Doe');
+    
+// Remove Common Name
+$dn->removeCn('John Doe');   
+```
+
+#### Setting a base
+
+If you'd like to set the base DN, such as a domain component RDN, use the `setBase()` method:
+
+```php
+$base = 'dc=corp,dc=acme,dc=org';
+
+$dn->setBase($base);
+```
+
+#### Creating a DN From A Model
+
+When you're creating a new AD record, you'll need to create a distinguished name as well. Let's go through an example of
+creating a new user.
+
+```php
+$user = $provider->make()->user();
+
+$user->setCommonName('John Doe');
+$user->setFirstName('John');
+$user->setLastName('Doe');
+```
+
+So we've set the basic information on the user, but we run into trouble when we want to put the user into a certain container
+(such as 'Accounting') which is done through the DN. Let's go through this example:
+
+```php
+$dn = $user->getDnBuilder();
+
+$dn->addCn($user->getCommonName());
+$dn->addOu('Accounting');
+$dn->addDc('corp');
+$dn->addDc('acme');
+$dn->addDc('org');
+
+// Returns 'cn=John Doe,ou=Accounting,dc=corp,dc=acme,dc=org'
+echo $dn->get();
+
+// The DistinguishedName object also contains the __toString() magic method
+// so you can also just echo the object itself
+echo $dn;
+```
+    
+Now we've built a DN, and all we have to do is set it on the new user:    
+
+```php
+$user->setDn($dn);
+
+$user->save();
+```
+
+#### Modifying a DN From A Model
+
+When you've received a model from a search result, you can build and modify the models DN like so:
+
+```php
+$user = $ad->users()->find('jdoe');
+
+$dn = $user->getDnBuilder();
+
+$dn->addOu('Users');
+
+$user->setDn($dn)->save();
+```

+ 32 - 0
api/vendor/adldap2/adldap2/docs/models/computer.md

@@ -0,0 +1,32 @@
+# The Computer Model
+
+> **Note**: This model contains the traits `HasDescription`, `HasLastLogonAndLogOff` & `HasCriticalSystemObject`.
+> For more information, visit the documentation:
+>
+> [HasDescription](traits/has-description.md),
+> [HasLastLogonAndLogOff](traits/has-last-login-last-logoff.md),
+> [HasCriticalSystemObject](traits/has-critical-system-object.md)
+
+## List of Available 'Getter' Methods:
+
+```php
+$computer = $provider->search()->computers()->find('ACME-EXCHANGE');
+
+// Returns 'Windows Server 2003'
+$computer->getOperatingSystem();
+
+// Returns '5.2 (3790)';
+$computer->getOperatingSystemVersion();
+
+// Returns 'Service Pack 1';
+$computer->getOperatingSystemServicePack();
+
+// Returns 'ACME-DESKTOP001.corp.acme.org'
+$computer->getDnsHostName();
+
+$computer->getLastLogOff();
+
+$computer->getLastLogon();
+
+$computer->getLastLogonTimestamp();
+```

+ 13 - 0
api/vendor/adldap2/adldap2/docs/models/contact.md

@@ -0,0 +1,13 @@
+# The Contact Model
+
+The Contact model extends from the base `Adldap\Models\Model` class and contains
+no specific methods / attributes that are limited to it.
+
+## Creation
+
+```php
+// Adldap\Models\Contact
+$contact = $provider->make()->contact([
+    'cn' => 'Suzy Doe',
+]);
+```

+ 24 - 0
api/vendor/adldap2/adldap2/docs/models/container.md

@@ -0,0 +1,24 @@
+# The Container Model
+
+> **Note**: This model contains the trait `HasDescription` & `HasCriticalSystemObject`.
+> For more information, visit the documentation:
+> 
+> [HasDescription](traits/has-description.md),
+> [HasCriticalSystemObject](traits/has-critical-system-object.md),
+
+## Creation
+
+```php
+// Adldap\Models\Container
+$container = $provider->make()->container([
+    'cn' => 'VPN Users',
+]);
+```
+
+## List of Available 'Getter' Methods:
+
+The `Container` model contains only one unique method.
+
+```php
+$flags = $container->getSystemFlags();
+```

+ 6 - 0
api/vendor/adldap2/adldap2/docs/models/entry.md

@@ -0,0 +1,6 @@
+# The Entry Model
+
+The Entry model extends from the base `Adldap\Models\Model` class and contains
+no specific methods / attributes that are limited to it.
+
+This class exists for LDAP records that don't contain a valid / recognized `objectClass` to map to a specific model.

+ 203 - 0
api/vendor/adldap2/adldap2/docs/models/group.md

@@ -0,0 +1,203 @@
+# The Group Model
+
+> **Note**: This model contains the trait `HasMemberOf`.
+> For more information, visit the documentation:
+>
+> [HasMemberOf](traits/has-member-of.md)
+
+## Creation
+
+```php
+// Adldap\Models\Group
+$group = $provider->make()->group([
+    'cn' => 'Managers',
+]);
+```
+
+## Getting a groups members
+
+When you receive a `Group` model instance, it will contain a `member`
+attribute which contains the distinguished names of all
+the members inside the group.
+
+```php
+$group = $provider->search()->groups()->first();
+
+foreach ($group->members as $member) {
+
+    // 'cn=John Doe,dc=corp,dc=acme,dc=org'
+    echo $member;
+
+}
+```
+
+But this might not be useful, since we might actually want the models for each member.
+
+This can be easily done with the `getMembers()` method on the group.
+
+```php
+$group = $provider->search()->groups()->first();
+
+foreach ($group->getMembers() as $member) {
+
+    // Will be an instance of a Adldap `Model`
+    $member->getCommonName();
+
+}
+```
+
+You should be aware however, that calling the `getMembers()` method will
+query your `AD` server for **every** member contained in
+the group to retrieve its model.
+
+Think of this example below as what is being called behind the scenes:
+
+```php
+$group = $provider->search()->groups()->first();
+
+foreach ($group->members as $member) {
+
+    $model = $provider->search()->findByDn($member);
+
+}
+```
+
+### Paginating Group members
+
+The group you're looking for might contain hundreds / thousands of members.
+
+In this case, your server might only return you a portion of the groups members.
+
+To get around this limit, you need to ask your server to paginate the groups members through a select:
+
+```php
+$group = $provider->search()->groups()->select('member;range=0-500')->first();
+
+foreach ($group->members as $member) {
+    // We'll only have 500 members in this query.
+}
+```
+
+Now, when we have the group instance, we'll only have the first `500` members inside this group. However, calling the `getMembers()` method will automatically retrieve the rest of the members for you:
+
+```php
+$group = $provider->search()->groups()->select('member;range=0-500')->first();
+
+foreach ($group->getMembers() as $member) {
+    
+    // Adldap will automatically retrieve the next 500 records until it's retrieved all records.
+    
+    $member->getCommonName();
+    
+}
+```
+
+## Getting only a groups member names
+
+To retrieve only the names of the members contained in a group, call the `getMemberNames()` method:
+
+```php
+foreach ($group->getMemberNames() as $name) {
+
+    // Returns 'John Doe' 
+    echo $name;
+
+}
+```
+
+> **Note**: This method does not query your server for each member to retrieve its name. It
+> only parses the distinguished names from the groups `member` attribute. This means that
+> if you have paginated group members, you will need to perform another query yourself
+> to retrieve the rest of the member names (or just call the `getMembers()` method).
+
+## Setting Group Members
+
+To set members that are apart of the group, you can perform this in two ways:
+
+> **Note**: Remember, this will remove **all** pre-existing members, and set the new given members on the group.
+
+```php
+$members = [
+    'cn=John Doe,dc=corp,dc=acme,dc=org',
+    'cn=Jane Doe,dc=corp,dc=acme,dc=org',
+];
+
+$group->setMembers($members);
+
+$group->save();
+```
+
+Or manually:
+
+```php
+$group->member = [
+    'cn=John Doe,dc=corp,dc=acme,dc=org',
+    'cn=Jane Doe,dc=corp,dc=acme,dc=org',
+];
+
+$group->save();
+```
+
+## Adding One Member
+
+To add a single member to a group, use the `addMember()` method:
+
+> **Note**: You do not need to call the `save()` method after adding a
+> member. It's automatically called so you can determine
+> if the member was successfully added.
+
+```php
+// We can provide a model, or just a plain DN of the new member
+$user = $provider->search()->users()->first();
+
+if ($group->addMember($user)) {
+
+    // User was successfully added to the group!
+
+}
+
+// Or
+
+$user = 'cn=John Doe,dc=corp,dc=acme,dc=org';
+
+if ($group->addMember($user)) {
+    //
+}
+```
+
+## Removing One Member
+
+To remove a single member to a group, use the `removeMember()` method:
+
+```php
+// We can provide a model, or just a plain DN of the existing member
+$group = $provider->search()->groups()->first();
+
+$member = $group->getMembers()->first();
+
+if ($group->removeMember($member)) {
+
+    // Member was successfully removed from the group!
+
+}
+
+// Or
+
+$user = 'cn=John Doe,dc=corp,dc=acme,dc=org';
+
+if ($group->removeMember($user)) {
+    //
+}
+```
+
+## Removing All Members
+
+To remove all members, use the `removeMembers()` method:
+
+```php
+if ($group->removeMembers()) {
+
+    // All members were successfully removed!
+
+}
+```

+ 625 - 0
api/vendor/adldap2/adldap2/docs/models/model.md

@@ -0,0 +1,625 @@
+# Models
+
+- [Creating](#creating)
+    - [Available Make Methods](#available-make-methods)
+- [Saving](#saving)
+    - [Creating Manually](#creating-manually)
+    - [Updating Manually](#updating-manually)
+- [Checking Existence](#checking-existence)
+- [Attributes](#attributes)
+    - [Getting Attributes](#getting-attributes)
+    - [Using a Getter](#using-a-getter)
+        - [Available Getters](#available-getters-on-all-models)
+    - [Getting Dirty Attributes](#getting-dirty-modified-attributes)
+    - [Getting Original Attributes](#getting-original-unmodified-attributes)
+    - [Setting Attributes](#setting-attributes)
+    - [Creating Attributes](#creating-attributes)
+    - [Updating Attributes](#updating-attributes)
+    - [Removing Attributes](#removing-attributes)
+    - [Checking Attributes](#checking-attributes)
+        - [Checking Existence of Attributes](#checking-existence-of-attributes)
+        - [Counting the Models Attributes](#counting-the-models-attributes)
+        - [Checking if a Model is Writable](#checking-if-a-model-is-writable)
+    - [Force Re-Syncing Attributes](#force-re-syncing-a-models-attributes)
+- [Moving / Renaming](#moving--renaming)
+- [Deleting](#deleting)
+- [Extending (Custom Models)](#extending)
+
+## Creating
+
+Creating LDAP entries manually is always a pain, but Adldap2 makes it effortless. Let's get started.
+
+When you have a provider instance, call the `make()` method. This returns an `Adldap\Models\Factory` instance:
+
+```php
+$factory = $provider->make();
+```
+
+Or you can chain all methods if you'd prefer:
+
+```php
+$user = $provider->make()->user();
+```
+
+### Available Make Methods:
+
+When calling a make method, all of them accept an `$attributes` parameter
+to fill the model with your specified attributes.
+
+```php
+// Adldap\Models\User
+$user = $provider->make()->user([
+    'cn' => 'John Doe',
+]);
+
+// Adldap\Models\Computer
+$computer = $provider->make()->computer([
+    'cn' => 'COMP-101',
+]);
+
+// Adldap\Models\Contact
+$contact = $provider->make()->contact([
+    'cn' => 'Suzy Doe',
+]);
+
+// Adldap\Models\Container
+$container = $provider->make()->container([
+    'cn' => 'VPN Users',
+]);
+
+// Adldap\Models\Group
+$group = $provider->make()->group([
+    'cn' => 'Managers',
+]);
+
+// Adldap\Models\OrganizationalUnit
+$ou = $provider->make()->ou([
+    'name' => 'Acme',
+]);
+```
+
+## Saving
+
+When you have any model instance, you can call the `save()` method to persist the
+changes to your server. This method returns a `boolean`. For example:
+
+```php
+$user = $provider->make()->user([
+    'cn' => 'New User',
+]);
+
+if ($user->save()) {
+    // User was saved.
+} else {
+    // There was an issue saving this user.
+}
+```
+
+> **Note**: When a model is saved successfully (whether created or updated),
+> the models attributes are re-synced in the background from your AD.
+> 
+> This allows you to perform other operations during the same
+> request that require an existing model.
+
+### Creating (Manually)
+
+If you are sure the model **does not exist** already inside your AD, you can use the `create()` method:
+
+```php
+$user = $provider->make()->user([
+    'cn' => 'New User',
+]);
+
+if ($user->create()) {
+    // User was created.
+} else {
+    // There was an issue creating this user.
+}
+```
+
+> **Note**: When you call the create method, if the model does not have a
+> distinguished name, one will automatically be generated for you using your
+> `base_dn` set in your configuration and the models common name.
+
+### Updating (Manually)
+
+If you are sure the model **does exist** already inside your AD, you can use the `update()` method:
+
+```php
+$user = $provider->search()->whereEquals('cn', 'John Doe')->firstOrFail();
+
+$user->displayName = 'Suzy Doe';
+
+if ($user->update()) {
+    // User was updated.
+} else {
+    // There was an issue updating this user.
+}
+```
+
+## Checking Existence
+
+If you need to check the existence of a model, use the property `exists`.
+
+How does it know if the model exists in AD? Well, when models are constructed from
+search results, the `exists` property on the model is set to `true`.
+
+```php
+$user = $provider->search()->find('jdoe');
+
+$user->exists; // Returns true.
+
+if ($user->delete()) {
+
+    $user->exists; // Returns false.
+
+}
+```
+
+If a model is created successfully, the `exists` property is set to `true`:
+
+```php
+$user = $provider->make()->user([
+    'cn' => 'John Doe',
+]);
+
+$user->exists; // Returns false.
+
+if ($user->save()) {
+    
+    $user->exists; // Returns true.
+    
+}
+```
+
+## Attributes
+
+Since all models extend from the base class `Adldap\Models\Model`, there are many
+useful methods that you can utilize on every model.
+
+### Getting Attributes
+
+You can get attributes in a few ways:
+
+```php
+// Returns an array all of the users attributes.
+$user->getAttributes();
+
+// Returns an array of all the users email addresses.
+// Returns `null` if non-existent.
+$user->getAttribute('mail');
+
+// Returns the users first email address.
+// Returns `null` if non-existent.
+$user->getAttribute('mail', 0);
+
+// Returns the users first email address.
+// Returns `null` if non-existent.
+$user->getFirstAttribute('mail');
+
+// Returns an array of all the users email addresses. 
+$user->mail;
+
+// Returns the users first email address.
+$user->mail[0];
+```
+
+#### Using a Getter
+
+Some attributes have methods for easier retrieval so you don't need to look up the LDAP attribute name.
+
+For example, to retrieve a users email address, use the method `getEmail()`:
+
+```php
+$user->getEmail();
+```
+
+Or to retrieve all of a users email addresses, use the method `getEmails()`:
+
+```php
+$user->getEmails();
+```
+
+##### Available Getters on All Models
+
+The following methods are available on all returned models:
+
+```php
+// Returns the model's 'name' attribute.
+$model->getName();
+
+// Returns the model's 'cn' attribute.
+$model->getCommonName();
+
+// Returns the model's 'displayname' attribute.
+$model->getDisplayName();
+
+// Returns the model's 'samaccountname' attriubte.
+$model->getAccountName();
+
+// Returns the model's 'samaccounttype` attribute.
+$model->getAccountType();
+
+// Returns the model's 'whencreated` attribute.
+$model->getCreatedAt();
+
+// Returns the model's 'whencreated` attribute in a MySQL timestamp format.
+$model->getCreatedAtDate();
+
+// Returns the model's 'whencreated' attribute in unix time.
+$model->getCreatedAtTimestamp();
+
+// Returns the model's 'whenchanged` attribute.
+$model->getUpdatedAt();
+
+// Returns the model's 'whenchanged` attribute in a MySQL timestamp format.
+$model->getUpdatedAtDate();
+
+// Returns the model's 'whenchanged` attribute in unix time.
+$model->getUpdatedAtTimestamp();
+
+// Returns the model's 'objectclass' attribute.
+$model->getObjectClass();
+
+// Returns the model's root object category string.
+$model->getObjectCategory();
+
+// Returns the model's object category in an array.
+$model->getObjectCategoryArray();
+
+// Returns the model's object category distinguished name.
+$model->getObjectCategoryDn();
+
+// Returns the model's SID in binary.
+$model->getObjectSid();
+
+// Returns the model's GUID in binary.
+$model->getObjectGuid();
+
+// Returns the model's SID in a string.
+$model->getConvertedSid();
+
+// Returns the model's GUID in a string.
+$model->getConvertedGuid();
+
+// Returns the model's primary group ID.
+$model->getPrimaryGroupId();
+
+// Returns the model's 'instancetype' attribute.
+$model->getInstanceType();
+
+// Returns the model's 'maxpwdage' attribute.
+$model->getMaxPasswordAge();
+```
+
+For more documentation on specific getters, please take a look at the relevant model documentation.
+
+#### Getting Dirty (Modified) Attributes
+
+You can get a models modified attributes using the `getDirty()` method:
+
+```php
+$user = $provider->search()->users()->find('john');
+
+// Returns array [0 => 'John Doe']
+var_dump($user->cn);
+
+$user->setAttribute('cn', 'Jane Doe');
+
+// Returns array ['cn' => [0 => 'Jane Doe']]
+var_dump($user->getDirty());
+
+// The attribute has been modified - returns array [0 => 'Jane Doe']
+var_dump($user->cn);
+```
+
+The method returns an array with the key being the modified attribute,
+and the array being the new values of the attribute.
+
+#### Getting Original (Unmodified) Attributes
+
+You can get a models original attributes using the `getOriginal()` method:
+
+```php
+$user = $provider->search()->users()->find('john');
+
+// Returns array [0 => 'John Doe']
+var_dump($user->cn);
+
+$user->setAttribute('cn', 'Jane Doe');
+
+// The attribute has been modified - returns array [0 => 'Jane Doe']
+var_dump($user->cn);
+
+// Retrieving the original value - returns array [0 => 'John Doe']
+var_dump($user->getOriginal()['cn']);
+```
+
+> **Note**: Keep in mind, when you `save()` a model, the models original
+> attributes will be re-synchronized to the models new attributes.
+
+### Setting Attributes
+
+Just like getting model attributes, there's multiple ways of setting attributes as well:
+
+```php
+// Setting via method:
+$user->setAttribute('cn', 'John Doe');
+
+// Specifying a subkey for overwriting specific attributes:
+$user->setAttribute('mail', 'other-mail@mail.com', 0);
+
+// Setting the first attribute:
+$user->setFirstAttribute('mail', 'jdoe@mail.com');
+
+// Setting via property:
+$user->cn = 'John Doe';
+
+// Mass setting attributes:
+$user->fill([
+    'cn' => 'John Doe',
+    'mail' => 'jdoe@mail.com',
+]);
+```
+
+### Creating Attributes
+
+To create an attribute that does not exist on the model, you can set it like a regular property:
+
+```php
+$user = $provider->search()->whereEquals('cn', 'John Doe')->firstOrFail();
+
+$user->new = 'New Attribute';
+
+$user->save();
+```
+
+If the set attribute does not exist on the model already,
+it will automatically be created when you call the `save()` method.
+
+If you'd like manually create new attributes individually, call the `createAttribute($attribute, $value)` method:
+
+```php
+if ($user->createAttribute('new', 'New Attribute')) {
+    // Attribute created.
+}
+```
+
+### Updating Attributes
+
+To modify an attribute you can either use a setter method, or by setting it manually:
+
+> **Note**: You can also utilize setters to create new attributes if your model does not already have the attribute.
+
+```php
+$user = $provider->search()->whereEquals('cn', 'John Doe')->firstOrFail();
+
+$user->cn = 'New Name';
+
+// Or use a setter:
+
+$user->setCommonName('New Name');
+
+$user->save();
+```
+
+If you'd like to update attributes individually, call the `updateAttribute($attribute, $value)` method:
+
+```php
+if ($user->updateAttribute('cn', 'New Name')) {
+    // Successfully updated attribute.
+}
+```
+
+### Removing Attributes
+
+To remove attributes, set the attribute to `NULL`:
+
+```php
+$user->cn = null;
+
+$user->save();
+```
+
+Or, you can call the `deleteAttribute($attribute)` method:
+
+```php
+if ($user->deleteAttribute('cn')) {
+    // Attribute has been deleted.
+}
+```
+
+### Checking Attributes
+
+#### Checking Existence of Attributes
+
+To see if a model contains an attribute, use the method `hasAttribute()`:
+
+```php
+// Checking if a base attribute exists:
+if ($user->hasAttribute('mail')) {
+
+    // This user contains an email address.
+
+}
+
+// Checking if a sub attribute exists, by key:
+if ($user->hasAttribute('mail', 1)) {
+ 
+    // This user contains a second email address.
+ 
+}
+```
+
+#### Counting the Models Attributes
+
+To retrieve the total number of attributes, use the method `countAttributes()`:
+
+```php
+$count = $user->countAttributes();
+
+var_dump($count); // Returns int
+```
+
+#### Checking if a Model is contained in an OU
+
+To check if a model is located inside an OU, use the `inOu()` method:
+
+```php
+if ($model->inOu('User Accounts')) {
+    // This model is inside the 'User Accounts' OU.
+}
+```
+
+You can also use an OU model instance:
+
+```php
+$serviceAccounts = $provider->search()->ous()->find('Service Accounts');
+
+if ($model->inOu($serviceAccounts)) {
+    // This model is inside the 'Service Accounts' OU.
+}
+```
+
+#### Checking if a Model is Writable
+
+To check if the model can be written to, use the method `isWritable()`:
+
+```php
+if ($model->isWritable()) {
+
+    // You can modify this model.
+    
+}
+```
+
+### Force Re-Syncing A Models Attributes
+
+If you need to forcefully re-sync a models attributes, use the method `syncRaw()`:
+
+```php
+$user->syncRaw();
+```
+
+> **Note**: This will query your AD server for the current model, and re-synchronize
+> it's attributes. This is only recommended if your creating / updating / deleting
+> attributes manually through your LDAP connection.
+
+## Moving / Renaming
+
+To move a user from one DN or OU to another, use the `move($newRdn, $newParentDn)` method:
+
+```php
+// New Relative distinguished name.
+$newRdn = 'cn=John Doe';
+
+// New parent distiguished name.
+$newParentDn = 'OU=New Ou,DC=corp,DC=local';
+
+if ($user->move($newRdn, $newParentDn)) {
+    // User was successfully moved to the new OU.
+}
+```
+
+If you would like to keep the models old RDN along side their new RDN, pass in false in the last parameter:
+
+```php
+// New Relative distinguished name.
+$newRdn = 'cn=John Doe';
+
+// New parent distiguished name.
+$newParentDn = 'OU=New Ou,DC=corp,DC=local';
+
+if ($user->move($newRdn, $newParentDn, $deleteOldRdn = false)) {
+    // User was successfully moved to the new OU,
+    // and their old RDN has been left in-tact.
+}
+```
+
+To rename a users DN, just pass in their new relative distinguished name in the `rename($newRdn)` method:
+
+```php
+$newRdn = 'cn=New Name';
+
+if ($user->rename($newRdn)) {
+    // User was successfully renamed.
+}
+```
+
+> **Note**: The `rename()` method is actually an alias for the `move()` method.
+
+## Deleting
+
+To delete a model, just call the `delete()` method:
+
+```php
+$user = $provider->search()->whereEquals('cn', 'John Doe')->firstOrFail();
+
+echo $user->exists; // Returns true.
+
+if ($user->delete()) {
+    // Successfully deleted user.
+
+    echo $user->exists; // Returns false.
+}
+```
+
+## Extending
+
+> **Note**: This feature was introduced in `v8.0.0`.
+
+To use your own models, you will need to create a new [Schema](../schema.md).
+
+Once you have created your own schema, you must insert it inside the construct of your provider.
+
+Let's walk through this process.
+
+First we'll create our model we'd like to extend / override:
+
+> **Note**: Your custom model **must** extend from an existing Adldap2 model.
+> This is due to methods and attributes that only exist on these classes.
+
+```php
+namespace App\Ldap\Models;
+
+use Adldap\Models\User as Model;
+
+class User extends Model
+{
+    public function getCommonName()
+    {
+        // Overriding model method.
+    }
+}
+```
+
+Now, we'll create our custom schema and return our models class name:
+
+```php
+namespace App\Ldap\Schemas;
+
+use App\Ldap\Models\User;
+
+class LdapSchema extends ActiveDirectory
+{
+    public function userModel()
+    {
+        return User::class;
+    }
+}
+```
+
+Finally, when we create a provider, we need to insert our Schema into the constructor:
+
+```php
+use Adldap\Connections\Provider;
+
+$schema = new LdapSchema();
+
+$provider = new Provider($config, $connection = null, $schema);
+
+$provider->connect();
+
+// If `jdoe` exists, your custom model will be returned.
+$user = $provider->search()->users()->find('jdoe');
+```

+ 27 - 0
api/vendor/adldap2/adldap2/docs/models/ou.md

@@ -0,0 +1,27 @@
+# The OrganizationalUnit Model
+
+The OrganizationalUnit model extends from the base `Adldap\Models\Model` class and contains
+no specific methods / attributes that are limited to it.
+
+## Creation
+
+```php
+// Adldap\Models\OrganizationalUnit
+$ou = $provider->make()->ou([
+    'name' => 'Workstation Computers',
+]);
+
+// Generate the OU's DN through the DN Builder:
+
+$dn = $ou->getDnBuilder();
+
+$dn->addOu('Workstation Computers');
+
+$ou->setDn($dn);
+
+// Or set the DN manually:
+
+$ou->setDn('ou=Workstation Computers,dc=test,dc=local,dc=com');
+
+$ou->save();
+```

+ 50 - 0
api/vendor/adldap2/adldap2/docs/models/printer.md

@@ -0,0 +1,50 @@
+# The Printer Model
+
+## List of Available 'Getter' Methods:
+
+```php
+
+$printer->getPrinterName();
+
+$printer->getPrinterShareName();
+
+$printer->getMemory();
+
+$printer->getUrl();
+
+$printer->getLocation();
+
+$printer->getServerName();
+
+$printer->getColorSupported();
+
+$printer->getDuplexSupported();
+
+$printer->getMediaSupported();
+
+$printer->getStaplingSupported();
+
+$printer->getPrintBinNames();
+
+$printer->getPrintMaxResolution();
+
+$printer->getPrintOrientations();
+
+$printer->getDriverName();
+
+$printer->getDriverVersion();
+
+$printer->getPriority();
+
+$printer->getPrintStartTime();
+
+$printer->getPrintEndTime();
+
+$printer->getPortName();
+
+$printer->getVersionNumber();
+
+$printer->getPrintRate();
+
+$printer->getPrintRateUnit();
+```

+ 33 - 0
api/vendor/adldap2/adldap2/docs/models/root-dse.md

@@ -0,0 +1,33 @@
+# The RootDse Model
+
+## Getting the Root DSE
+
+To get the Root DSE of your AD server, call the `getRootDse()` method off a new search:
+
+```php
+$rootDse = $provider->search()->getRootDse();
+```
+
+## Getting the schema naming context
+
+To get the Root DSE schema naming context, call the `getSchemaNamingContext()`:
+
+```php
+$rootDse = $provider->search()->getRootDse();
+
+$context = $rootDse->getSchemaNamingContext();
+
+// Returns 'cn=Schema,cn=Configuration,dc=corp,dc=acme,dc=org'
+echo $context;
+```
+
+## Getting the root domain naming context:
+
+To get the Root DSE domain naming context, call the `getRootDomainNamingContext()`:
+
+```php
+$context = $rootDse->getRootDomainNamingContext();
+
+// Returns 'dc=corp,dc=acme,dc=org'
+echo $context;
+```

+ 13 - 0
api/vendor/adldap2/adldap2/docs/models/traits/has-critical-system-object.md

@@ -0,0 +1,13 @@
+# HasCriticalSystemObject Trait
+
+Models that contain this trait, have the `isCriticalSystemObject` attribute.
+
+There is only one method that accompanies this trait:
+
+```php
+if ($model->isCriticalSystemObject()) {
+
+    //
+
+}
+```

+ 11 - 0
api/vendor/adldap2/adldap2/docs/models/traits/has-description.md

@@ -0,0 +1,11 @@
+# HasDescription Trait
+
+Models that contain this trait, have the `description` attribute.
+
+There are only two methods that accompany this trait:
+
+```php
+$model->getDescription();
+
+$model->setDescription('The models description');
+```

+ 2 - 0
api/vendor/adldap2/adldap2/docs/models/traits/has-last-login-last-logoff.md

@@ -0,0 +1,2 @@
+# HasLastLoginAndLastLogoff
+

+ 166 - 0
api/vendor/adldap2/adldap2/docs/models/traits/has-member-of.md

@@ -0,0 +1,166 @@
+# HasMemberOf Trait
+
+Models that contain this trait, have the ability to be apart of a group.
+
+There's many helpful methods to assist you in all of the operations related to group membership, let's get started!
+
+## Retrieving Groups
+
+To retrieve the groups that a model is apart of, call the `getGroups()` method:
+
+```php
+$user = $provider->search()->users()->find('jdoe');
+
+$groups = $user->getGroups();
+
+foreach ($groups as $group) {
+
+    $group->getCommonName(); // ex. 'Accounting'
+
+}
+```
+
+We can also pass in specific fields we need from the returned groups to speed up our queries.
+
+For example, if we only need the groups common name:
+
+```php
+// Group models will be returned with only their common name.
+$groups = $user->getGroups(['cn']);
+```
+
+However, calling `getGroups()` will only retrieve the models immediate groups (non-recursive).
+
+To retrieve nested groups, pass in `true` into the second parameter:
+
+```php
+$groups = $user->getGroups([], $recursive = true);
+```
+
+## Retrieve Group Names
+
+If you only want the models group names, call the `getGroupNames()` method:
+
+```php
+$names = $user->getGroupNames();
+
+foreach ($names as $name) {
+
+    echo $name; // ex. 'Accounting'
+
+}
+```
+
+However, this method will also retrieve only the immediate groups names
+much like the `getGroups()` method. You'll need to pass in `true` in
+the first parameter to retrieve results recursively.
+
+```php
+$names = $user->getGroupNames($recursive = true);
+```
+
+## Checking if the Model is apart of a Group
+
+To check if a model is apart of a certain group, use the `inGroup()` method:
+
+```php
+$group = $provider->search()->groups()->find('Office');
+
+if ($user->inGroup($group)) {
+
+    //
+
+}
+```
+
+You can also check for multiple memberships by passing in an array of groups:
+
+```php
+$groups = $provider->search()->findManyBy('cn', ['Accounting', 'Office']));
+
+if ($user->inGroup($groups->toArray()) {
+    
+    // This user is apart of the 'Accounting' and 'Office' group!
+
+}
+```
+
+> **Note**: Much like the other methods above, you'll need to provide a `$recursive`
+> flag to the `inGroup()` method if you'd like recursive results included.
+
+We can also provide distinguished names instead of Group model instances:
+
+```php
+$dns = [
+    'cn=Accounting,ou=Groups,dc=acme,dc=org',
+    'cn=Office,ou=Groups,dc=acme,dc=org',
+];
+
+if ($user->inGroup($dns, $recursive = true)) {
+    
+    //
+
+}
+```
+
+Or, we can also just provide the name(s) of the group(s).
+
+```php
+$names = [
+    'Accounting',
+    'Office',
+];
+
+if ($user->inGroup($names, $recursive = true)) {
+    
+    //
+
+}
+```
+
+## Adding a Group
+
+To add the model to a specific group, call the `addGroup()` method:
+
+```php
+$group = $provider->search()->groups()->find('Accounting');
+
+// You can either provide a Group model:
+if ($user->addGroup($group)) {
+
+    //
+
+}
+
+// Or a Groups DN:
+if ($user->addGroup('cn=Accounting,ou=Groups,dc=acme,dc=org')) {
+
+    //
+
+}
+```
+
+> **Note**: You do not need to call the `save()` method for adding / removing groups.
+> This is done automatically so you can perform clean `if` statements on the method.
+
+## Removing a Group
+
+To remove the model from a specific group, call the `removeGroup()` method:
+
+```php
+$group = $user->getGroups()->first();
+
+// You can either provide a Group model:
+if ($user->removeGroup($group)) {
+
+    //
+
+}
+
+// Or the groups DN:
+if ($user->removeGroup('cn=Accounting,ou=Office Groups,dc=acme,dc=org')) {
+
+    //
+
+}
+```

+ 138 - 0
api/vendor/adldap2/adldap2/docs/models/user.md

@@ -0,0 +1,138 @@
+# The User Model
+
+> **Note**: This model contains the trait `HasMemberOf`. For more information, visit the documentation:
+> [HasMemberOfTrait](traits/has-member-of.md)
+
+## List of Available 'Getter' Methods:
+
+There's a ton of available getter methods for the User model. Below is a list for a quick reference.
+
+> **Note**: Don't see a method for an AD attribute? Create an issue and let us know!
+
+```php
+// Get the users display name.
+$user->getDisplayName();
+
+// Get the users first email address.
+$user->getEmail();
+
+// Get the users email addresses, if they have multiple.
+$user->getEmails();
+
+// Get the users title.
+$user->getTitle();
+
+// Get the users department.
+$user->getDepartment();
+
+// Get the users first name.
+$user->getFirstName();
+
+// Get the users last name.
+$user->getLastName();
+
+// Get the users info.
+$user->getInfo();
+
+// Get the users initials.
+$user->getInitials();
+
+// Get the users country.
+$user->getCountry();
+
+// Get the users street address.
+$user->getStreetAddress();
+
+// Get the users postal code.
+$user->getPostalCode();
+
+// Get the users physical delivery office name.
+$user->getPhysicalDeliveryOfficeName();
+
+// Get the users phone number.
+$user->getTelephoneNumber();
+
+// Get the users locale.
+$user->getLocale();
+
+// Get the users company.
+$user->getCompany();
+
+// Get the users other email addresses.
+$user->getOtherMailbox();
+
+// Get the users home mailbox database location (stored as a distinguished name). 
+$user->getHomeMdb();
+
+// Get the users email nickname.
+$user->getMailNickname();
+
+// Get the users principal name.
+$user->getUserPrincipalName();
+
+// Get the users proxy email addresses.
+$user->getProxyAddresses();
+
+// Get the users failed login attempts.
+$user->getBadPasswordCount();
+
+// Get the users last failed login attempt timestamp.
+$user->getBadPasswordTime();
+
+// Get the users last password change timestamp.
+$user->getPasswordLastSet();
+
+// Get the users last password change timestamp in unix time.
+$user->getPasswordLastSetTimestamp();
+
+// Get the users last password change timestamp in MySQL date format.
+$user->getPasswordLastSetDate();
+
+// Get the users lockout time.
+$user->getLockoutTime();
+
+// Get the users user account control integer.
+$user->getUserAccountControl();
+
+// Get the users roaming profile path.
+$user->getProfilePath();
+
+// Get the users legaxy exchange distinguished name.
+$user->getLegacyExchangeDn();
+
+// Get the users account expiry timestamp.
+$user->getAccountExpiry();
+
+// Get the boolean that determines whether to show this user in the global address book.
+$user->getShowInAddressBook();
+
+// Get the users thumbnail photo.
+$user->getThumbnail();
+
+// Get the users thumbnail photo (base64 encoded for HTML <img src=""> tags).
+$user->getThumbnailEncoded();
+
+// Get the users jpeg photo.
+$user->getJpegPhoto();
+
+// Get the users jpeg photo (base64 encoded for HTML <img src=""> tags).
+$user->getJpegPhotoEncoded();
+
+// Get the users manager.
+$user->getManager();
+
+// Get the users employee ID.
+$user->getEmployeeId();
+
+// Get the users employee number.
+$user->getEmployeeNumber();
+
+// Get the users room number.
+$user->getRoomNumber();
+
+// Get the users department number.
+$user->getDepartmentNumber();
+
+// Get the users personal title.
+$user->getPersonalTitle();
+```

+ 640 - 0
api/vendor/adldap2/adldap2/docs/query-builder.md

@@ -0,0 +1,640 @@
+# Query Builder
+
+## Index
+
+- [Selects](#selects)
+- [Limit](#limit)
+- [Wheres](#wheres)
+- [Or Wheres](#or-wheres)
+- [Dynamic Wheres](#dynamic-wheres)
+- [Nested Filters](#nested-filters)
+- [Raw Filters](#raw-filters)
+- [Sorting](#sorting)
+- [Pagination](#paginating)
+- [Scopes](#scopes)
+- [Base DN](#base-dn)
+- [Search Options](#search-options)
+
+## Introduction
+
+The Adldap2 query builder makes building LDAP queries feel effortless.
+
+It allows you to generate queries using a fluent and convenient interface.
+
+> **Note:** The Adldap2 query builder escapes all fields & values
+> given to its `where()` methods. There is no need to clean or
+> escape strings before passing them into the query builder.
+
+## Creating a new Query
+
+To create a new search query, call the `search()` method on your provider instance:
+
+```php
+$search = $provider->search();
+```
+
+Or you can chain all your methods if you'd prefer:
+
+```php
+$results = $provider->search()->where('cn', '=', 'John Doe')->get();
+```
+
+## Selects
+
+> **Note:** Fields are case in-sensitive. For example, you can
+> insert `CN`, `cn` or `cN`, they will return the same result.
+
+#### Selecting attributes
+
+Selecting only the LDAP attributes you need will increase the speed of your queries.
+
+```php
+// Passing in an array of attributes
+$search->select(['cn', 'samaccountname', 'telephone', 'mail']);
+
+// Passing in each attribute as an argument
+$search->select('cn', 'samaccountname', 'telephone', 'mail');
+```
+
+#### Finding a specific record
+
+If you're trying to find a single record, but not sure what the record might be, use the `find()` method:
+
+```php
+$record = $search->find('John Doe');
+
+if ($record) {
+    // Record was found!    
+} else {
+    // Hmm, looks like we couldn't find anything...
+}
+```
+
+> **Note**: Using the `find()` method will search for LDAP records using ANR (ambiguous name resolution).
+> For a more fine-tuned search, use the `findBy()` method below.
+
+##### Finding a specific record (or failing)
+
+If you'd like to try and find a single record and throw an exception when it hasn't been
+found, use the `findOrFail()` method:
+
+```php
+try {
+
+    $record = $search->findOrFail('John Doe');
+    
+} catch (\Adldap\Models\ModelNotFoundException $e) {
+    // Record wasn't found!
+}
+```
+
+#### Finding a specific record by a specific attribute
+
+If you're looking for a single record with a specific attribute, use the `findBy()` method:
+
+```php
+// We're looking for a record with the 'samaccountname' of 'jdoe'.
+$record = $search->findBy('samaccountname', 'jdoe');
+```
+
+##### Finding a specific record by a specific attribute (or failing)
+
+If you'd like to try and find a single record by a specific attribute and throw
+an exception when it hasn't been found, use the `findByOrFail()` method:
+
+```php
+try {
+
+    $record = $search->findByOrFail('samaccountname', 'jdoe');
+    
+} catch (\Adldap\Models\ModelNotFoundException $e) {
+    // Record wasn't found!
+}
+```
+
+#### Finding a specific record by its distinguished name
+
+If you're looking for a single record with a specific DN, use the `findByDn()` method:
+
+```php
+$record = $search->findByDn('cn=John Doe,dc=corp,dc=org');
+```
+
+###### Finding a specific record by its distinguished name (or failing)
+
+If you'd like to try and find a single record by a specific DN and throw
+an exception when it hasn't been found, use the `findByDnOrFail()` method:
+
+```php
+try {
+
+    $record = $search->findByDnOrFail('cn=John Doe,dc=corp,dc=org');
+    
+} catch (\Adldap\Models\ModelNotFoundException $e) {
+    // Record wasn't found!
+}
+```
+
+#### Retrieving results
+
+To get the results from a search, simply call the `get()` method:
+
+```php
+$results = $search->select(['cn', 'samaccountname'])->get();
+```
+
+##### Retrieving all LDAP records
+
+To get all records from LDAP, call the `all()` method:
+
+```php
+$results = $search->all();
+```
+
+##### Retrieving the first record
+
+To retrieve the first record of a search, call the `first()` method:
+
+```php
+$record = $search->first();
+```
+
+###### Retrieving the first record (or failing)
+
+To retrieve the first record of a search or throw an exception when one isn't found, call the `firstOrFail()` method:
+
+```php
+try {
+
+    $record = $search->firstOrFail();
+    
+} catch (\Adldap\Models\ModelNotFoundException $e) {
+    // Record wasn't found!
+}
+```
+
+## Limit
+
+To limit the results records returned from your LDAP server and increase the
+speed of your queries, you can use the `limit()` method:
+
+```php
+// This will only return 5 records that contain the name of 'John'.
+$records = $search->where('cn', 'contains', 'John')->limit(5)->get();
+```
+
+## Wheres
+
+To perform a where clause on the search object, use the `where()` function:
+
+```php
+$search->where('cn', '=', 'John Doe');
+```
+
+This query would look for a record with the common name of 'John Doe' and return the results.
+
+We can also perform a 'where equals' without including the operator:
+
+```php
+$search->whereEquals('cn', 'John Doe');
+```
+
+We can also supply an array of key - value pairs to quickly add multiple wheres:
+
+```php
+$wheres = [
+    'cn' => 'John Doe',
+    'samaccountname' => 'jdoe',
+];
+
+$search->where($wheres);
+```
+
+Or, if you require conditionals, you can quickly add multiple wheres with nested arrays:
+
+```php
+$search->where([
+   ['cn', '=', 'John Doe'],
+   ['manager', '!', 'Suzy Doe'],
+]);
+```
+
+#### Where Starts With
+
+We could also perform a search for all objects beginning with the common name of 'John' using the `starts_with` operator:
+
+```php
+$results = $provider->search()->where('cn', 'starts_with', 'John')->get();
+
+// Or use the method whereStartsWith($attribute, $value)
+
+$results = $provider->search()->whereStartsWith('cn', 'John')->get();
+```
+
+#### Where Ends With
+    
+We can also search for all objects that end with the common name of `Doe` using the `ends_with` operator:
+
+```php
+$results = $provider->search()->where('cn', 'ends_with', 'Doe')->get();
+
+// Or use the method whereEndsWith($attribute, $value)
+
+$results = $provider->search()->whereEndsWith('cn', 'Doe')->get();
+```
+
+#### Where Between
+
+To search for records between two values, use the `whereBetween` method.
+
+For the example below, we'll retrieve all users who were created between two dates:
+
+```php
+$from = (new DateTime('October 1st 2016'))->format('YmdHis.0\Z');
+$to = (new DateTime('January 1st 2017'))->format('YmdHis.0\Z');
+
+$users = $provider->search()
+    ->users()
+    ->whereBetween('whencreated', [$from, $to])
+    ->get();
+```
+
+#### Where Contains
+
+We can also search for all objects with a common name that contains `John Doe` using the `contains` operator:
+
+```php
+$results = $provider->search()->where('cn', 'contains', 'John Doe')->get();
+
+// Or use the method whereContains($attribute, $value)
+
+$results = $provider->search()->whereContains('cn', 'John Doe')->get();
+```
+
+##### Where Not Contains
+
+You can use a 'where not contains' to perform the inverse of a 'where contains':
+
+```php
+$results = $provider->search()->where('cn', 'not_contains', 'John Doe')->get();
+
+// Or use the method whereNotContains($attribute, $value)
+
+$results = $provider->search()->whereNotContains('cn', 'John Doe');
+```
+
+#### Where Has
+
+Or we can retrieve all objects that have a common name attribute using the wildcard operator (`*`):
+
+```php
+$results = $provider->search()->where('cn', '*')->get();
+
+// Or use the method whereHas($field)
+$results = $provider->search()->whereHas('cn')->get();
+```
+
+This type of filter syntax allows you to clearly see what your searching for.
+
+##### Where Not Has
+
+You can use a 'where not has' to perform the inverse of a 'where has':
+
+```php
+$results = $provider->search->where('cn', '!*')->get();
+
+// Or use the method whereNotHas($field)
+$results = $provider->search()->whereNotHas($field)->get();
+```
+
+## Or Wheres
+
+To perform an 'or where' clause on the search object, use the `orWhere()` function. However, please be aware this
+function performs differently than it would on a database. For example:
+
+```php
+$results = $search
+            ->where('cn', '=', 'John Doe')
+            ->orWhere('cn', '=', 'Suzy Doe')
+            ->get();
+```
+    
+This query would return no results, because we're already defining that the common name (`cn`) must equal `John Doe`. Applying
+the `orWhere()` does not amount to 'Look for an object with the common name as "John Doe" OR "Suzy Doe"'. This query would
+actually amount to 'Look for an object with the common name that <b>equals</b> "John Doe" OR "Suzy Doe"
+
+To solve the above problem, we would use `orWhere()` for both fields. For example:
+
+```php
+$results = $search
+        ->orWhere('cn', '=', 'John Doe')
+        ->orWhere('cn', '=', 'Suzy Doe')
+        ->get();
+```
+
+Now, we'll retrieve both John and Suzy's AD records, because the common name can equal either.
+
+> **Note**: You can also use all `where` methods as an or where, for example:
+`orWhereHas()`, `orWhereContains()`, `orWhereStartsWith()`, `orWhereEndsWith()`
+
+## Dynamic Wheres
+
+> **Note**: This feature was introduced in `v6.0.16`.
+
+To perform a dynamic where, simply suffix a `where` with the field you're looking for.
+
+This feature was directly ported from Laravel's Eloquent.
+
+Here's an example:
+
+```php
+// This query:
+$result = $search->where('cn', '=', 'John Doe')->first();
+
+// Can be converted to:
+$result = $search->whereCn('John Doe')->first();
+```
+
+You can perform this on **any** attribute:
+
+```php
+$result = $search->whereTelephonenumber('555-555-5555')->first();
+```
+
+You can also chain them:
+
+```php
+$result = $search
+    ->whereTelephonenumber('555-555-5555')
+    ->whereGivenname('John Doe')
+    ->whereSn('Doe')
+    ->first();
+```
+
+You can even perform multiple dynamic wheres by separating your fields by an `And`:
+
+```php
+// This would perform a search for a user with the
+// first name of 'John' and last name of 'Doe'.
+$result = $search->whereGivennameAndSn('John', 'Doe')->first();
+```
+
+## Nested Filters
+
+By default, the Adldap2 query builder automatically wraps your queries in `and` / `or` filters for you.
+However, if any further complexity is required, nested filters allow you
+to construct any query fluently and easily.
+
+#### andFilter
+
+The `andFilter` method accepts a closure which allows you to construct a query inside of an `and` LDAP filter:
+
+```php
+$query = $provider->search()->newQuery();
+
+// Creates the filter: (&(givenname=John)(sn=Doe))
+$results = $query->andFilter(function (\Adldap\Query\Builder $q) {
+
+    $q->where('givenname', '=', 'John')
+      ->where('sn', '=', 'Doe');
+      
+})->get();
+```
+
+The above query would return records that contain the first name `John` **and** the last name `Doe`.
+
+#### orFilter
+
+The `orFilter` method accepts a closure which allows you to construct a query inside of an `or` LDAP filter:
+
+```php
+$query = $provider->search()->newQuery();
+
+
+// Creates the filter: (|(givenname=John)(sn=Doe))
+$results = $query->orFilter(function (\Adldap\Query\Builder $q) {
+
+    $q->where('givenname', '=', 'John')
+      ->where('sn', '=', 'Doe');
+      
+})->get();
+```
+
+The above query would return records that contain the first name `John` **or** the last name `Doe`.
+
+#### notFilter
+
+The `notFilter` method accepts a closure which allows you to construct a query inside a `not` LDAP filter:
+
+```php
+$query = $provider->search()->newQuery();
+
+// Creates the filter: (!(givenname=John)(sn=Doe))
+$results = $query->notFilter(function (\Adldap\Query\Builder $q) {
+
+    $q->where('givenname', '=', 'John')
+      ->where('sn', '=', 'Doe');
+      
+})->get();
+```
+
+The above query would return records that **do not** contain the first name `John` **or** the last name `Doe`.
+
+#### Complex Nesting
+
+The above methods `andFilter` / `orFilter` can be chained together and nested
+as many times as you'd like for larger complex queries:
+
+```php
+$query = $provider->search()->newQuery();
+
+$query = $query->orFilter(function (\Adldap\Query\Builder $q) {
+    $q->where('givenname', '=', 'John')
+        ->where('sn', '=', 'Doe');
+})->andFilter(function (\Adldap\Query\Builder $q) {
+    $q->where('department', '=', 'Accounting')
+        ->where('title', '=', 'Manager');
+})->getUnescapedQuery();
+
+echo $query; // Returns '(&(|(givenname=John)(sn=Doe))(&(department=Accounting)(title=Manager)))'
+```
+
+## Raw Filters
+
+> **Note**: Raw filters are not escaped. **Do not** accept user input into the raw filter method.
+
+Sometimes you might just want to add a raw filter without using the query builder.
+You can do so by using the `rawFilter()` method:
+
+```php
+$filter = '(samaccountname=jdoe)';
+
+$results = $search->rawFilter($filter)->get();
+
+// Or use an array
+$filters = [
+    '(samaccountname=jdoe)',
+    '(surname=Doe)',
+];
+
+$results = $search->rawFilter($filters)->get();
+
+// Or use multiple arguments
+$results = $search->rawFilter($filters[0], $filters[1])->get();
+
+// Multiple raw filters will be automatically wrapped into an `and` filter:
+$query = $search->getUnescapedQuery();
+
+echo $query; // Returns (&(samaccountname=jdoe)(surname=Doe))
+```
+
+## Sorting
+
+Sorting is really useful when your displaying tabular AD results. You can
+easily perform sorts on any AD attribute by using the `sortBy()` method:
+
+```php
+$results = $search->whereHas('cn')->sortBy('cn', 'asc')->get();
+```
+
+You can also sort paginated results:
+
+```php
+$results = $search->whereHas('cn')->sortBy('cn', 'asc')->paginate(25);
+```
+
+## Paginating
+
+Paginating your search results will allow you to return more results than your LDAP cap
+(usually 1000) and display your results in pages.
+
+> **Note**: Pagination will retrieve **all** records from your LDAP server.
+> The pagination object is simply a collection that allows you to
+> iterate through the resulting records easily and intuitively.
+
+To perform this, call the `paginate()` method instead of the `get()` method:
+
+```php
+$recordsPerPage = 50;
+$currentPage = $_GET['page'];
+
+// This would retrieve all records from AD inside a new Adldap\Objects\Paginator instance.
+$paginator = $search->paginate($recordsPerPage, $currentPage);
+
+// Returns total number of pages, int
+$paginator->getPages();
+
+// Returns current page number, int
+$paginator->getCurrentPage();
+
+// Returns the amount of entries allowed per page, int
+$paginator->getPerPage();
+
+// Returns all of the results in the entire paginated result
+$paginator->getResults();
+
+// Returns the total amount of retrieved entries, int
+$paginator->count();
+
+// Iterate over the results like normal
+foreach($paginator as $result)
+{
+    echo $result->getCommonName();
+}
+```
+
+## Scopes
+
+Search scopes allow you to easily retrieve common models of a particular 'scope'. Here is how you utilize them:
+
+```php
+// Retrieve all users.
+$results = $search->users()->get();
+
+// Retrieve all printers.
+$results = $search->printers()->get();
+
+// Retrieve all organizational units.
+$results = $search->ous()->get();
+
+// Retrieve all groups.
+$results = $search->groups()->get();
+
+// Retrieve all containers.
+$results = $search->containers()->get();
+
+// Retrieve all contacts.
+$results = $search->contacts()->get();
+
+// Retrieve all computers.
+$results = $search->computers()->get();
+```
+
+## Base DN
+
+To set the base DN of your search you can use one of two methods:
+
+```php
+// Using the `in()` method:
+$results = $provider->search()
+    ->in('ou=Accounting,dc=acme,dc=org')
+    ->get();
+    
+// Using the `setDn()` method:
+$results = $provider->search()
+    ->setDn('ou=Accounting,dc=acme,dc=org')
+    ->get();
+```
+
+Either option will return the same results. Use which ever method you prefer to be more readable.
+
+## Search Options
+
+#### Recursive
+
+By default, all searches performed are recursive. If you'd like to disable recursive search, use the `recursive()` method:
+
+```php
+$result = $provider->search()->recursive(false)->all();
+```
+    
+This would perform an `ldap_listing()` instead of an `ldap_search()`.
+
+#### Read
+
+If you'd like to perform a read instead of a listing or a recursive search, use the `read()` method:
+
+```php
+$result = $provider->search()->read(true)->where('objectClass', '*')->get();
+```
+
+This would perform an `ldap_read()` instead of an `ldap_listing()` or an `ldap_search()`.
+
+#### Raw
+
+If you'd like to retrieve the raw LDAP results, use the `raw()` method:
+
+```php
+$rawResults = $provider->search()->raw()->where('cn', '=', 'John Doe')->get();
+
+var_dump($rawResults); // Returns an array
+```
+
+## Retrieving the ran query
+
+If you'd like to retrieve the current query to save or run it at another time, use the `getQuery()` method
+on the query builder. This will return the escaped filter.
+
+```php
+$query = $provider->search()->where('cn', '=', 'John Doe')->getQuery();
+
+echo $query; // Returns '(cn=\4a\6f\68\6e\20\44\6f\65)'
+```
+
+You can also utilize the `getUnescapedQuery()` method for retrieving the unescaped filter:
+
+```php
+$query = $provider->search()->where('cn', '=', 'John Doe')->getUnescapedQuery();
+
+echo $query; // Returns '(cn=John Doe)'
+```

+ 69 - 0
api/vendor/adldap2/adldap2/docs/quick-start.md

@@ -0,0 +1,69 @@
+## Quick Start
+
+### Installation
+
+Adldap2 requires [Composer](https://getcomposer.org/) for installation.
+
+Once you have composer installed, run the following command in the root directory of your project:
+
+```
+composer require adldap2/adldap2
+```
+
+You're all set!
+
+### Usage
+
+```php
+// Construct new Adldap instance.
+$ad = new \Adldap\Adldap();
+
+// Create a configuration array.
+$config = [  
+  // The domain controllers option is an array of your LDAP hosts. You can
+  // use the either the host name or the IP address of your host.
+  'domain_controllers'    => ['ACME-DC01.corp.acme.org', '192.168.1.1'],
+  
+  // The base distinguished name of your domain.
+  'base_dn'               => 'dc=corp,dc=acme,dc=org',
+  
+  // The account to use for querying / modifying LDAP records. This
+  // does not need to be an actual admin account. This can also
+  // be a full distinguished name of the user account.
+  'admin_username'        => 'admin@corp.acme.org',
+  'admin_password'        => 'password',
+];
+
+// Add a connection provider to Adldap.
+$ad->addProvider($config);
+
+try {
+    // If a successful connection is made to your server, the provider will be returned.
+    $provider = $ad->connect();
+
+    // Performing a query.
+    $results = $provider->search()->where('cn', '=', 'John Doe')->get();
+    
+    // Finding a record.
+    $user = $provider->search()->find('jdoe');
+
+    // Creating a new LDAP entry. You can pass in attributes into the make methods.
+    $user =  $provider->make()->user([
+        'cn'          => 'John Doe',
+        'title'       => 'Accountant',
+        'description' => 'User Account',
+    ]);
+
+    // Setting a model's attribute.
+    $user->cn = 'John Doe';
+
+    // Saving the changes to your LDAP server.
+    if ($user->save()) {
+        // User was saved!
+    }
+} catch (\Adldap\Auth\BindException $e) {
+
+    // There was an issue binding / connecting to the server.
+
+}
+```

+ 53 - 0
api/vendor/adldap2/adldap2/docs/schema.md

@@ -0,0 +1,53 @@
+# Schema
+
+In Adldap2, a Schema class has been implemented. This means that if your
+active directory schema differs is some way for specific attributes,
+you can customize them and those attribute names and it will
+persist throughout using Adldap2. The schema also provides
+a convenient way of accessing Schema attributes.
+
+Let's get started.
+
+Adldap2 comes with an `Adldap\Schemas\ActiveDirectory` schema by default, which implements `Adldap\Schemas\SchemaInterface`.
+
+You can either extend from the `ActiveDirectory` schema, or create your own and implement the `SchemaInterface`.
+
+Please browse the [Schema Interface](/src/Schemas/SchemaInterface.php) to view all of the schema methods.
+
+Your Schema:
+
+```php
+namespace App\Schemas;
+
+use Adldap\Schemas\ActiveDirectory;
+
+class MySchema extends ActiveDirectory
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategory()
+    {
+        return 'objectcategory';
+    }
+}
+```
+
+Injecting your custom schema:
+
+```php
+// Your configuration array.
+$config = ['...'];
+
+// New up your custom schema.
+$mySchema = new \App\Schema\MySchema();
+
+// Create a new connection provider, and inject your schema.
+$provider = new \Adldap\Connections\Provider($config, $connection = null, $mySchema);
+
+// Add the provider to your Adldap instance.
+$adldap->addProvider($provider, $name = 'default');
+
+// Connect to your provider.
+$adldap->connect('default');
+```

+ 79 - 0
api/vendor/adldap2/adldap2/docs/troubleshooting.md

@@ -0,0 +1,79 @@
+# Troubleshooting
+
+#### Creating and Setting a Users Password
+
+To set a users password when you've created a new one, you need to enable their account, **then** set their password.
+
+For example:
+
+```php
+// Construct a new user instance.
+$user = $provider->make()->user();
+
+// Set the user profile details.
+$user->setAccountName('jdoe');
+$user->setFirstName('John');
+$user->setLastName('Doe');
+$user->setCompany('ACME');
+$user->setEmail('jdoe@acme.com');
+
+// Save the new user.
+if ($user->save()) {
+    // Enable the new user (using user account control).
+    $user->setUserAccountControl(512);
+
+    // Set new user password
+    $user->setPassword('Password123');
+
+    // Save the user.
+    if($user->save()) {
+        // The password was saved successfully.
+    }
+}
+```
+
+#### Retrieving All Records Inside a Group
+
+To retrieve all records inside a particular group (including nested groups), use the `rawFilter()` method:
+
+```php
+// The `memberof:1.2.840.113556.1.4.1941:` string indicates
+// that we want all nested group records as well.
+$filter = '(memberof:1.2.840.113556.1.4.1941:=CN=MyGroup,DC=example,DC=com)';
+
+$users = $provider->search()->rawFilter($filter)->get();
+```
+
+#### I'm connected but not getting any search results!
+
+The first thing you need to ensure is your `base_dn` in your configuration.
+
+Your `base_dn` needs to identical to the base DN on your domain. Even one mistyped character will result in no search results.
+
+If you also include an `ou` in your base DN (ex. `ou=Accounting,dc=corp,dc=acme,dc=org`), you will only receive results inside the `Accounting` OU.
+
+Once you're connected to your LDAP server, retrieve the Root DSE record.
+
+Here's a full example:
+
+```php
+$providers = [
+    'default' => [
+        'base_dn' => '',
+        '...',
+    ]
+];
+
+$ad = new \Adldap\Adldap($providers);
+
+try {
+    $provider = $ad->connect();
+    
+    $root = $provider->search()->getRootDse();
+    
+    // ex. Returns 'dc=corp,dc=acme,dc=org'
+    die($root->getRootDomainNamingContext());
+
+} catch (\Adldap\Auth\BindException $e) {
+}
+```

+ 8 - 0
api/vendor/adldap2/adldap2/license.md

@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright © Steve Bauman
+
+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.

+ 18 - 0
api/vendor/adldap2/adldap2/phpunit.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         bootstrap="vendor/autoload.php"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+        >
+    <testsuites>
+        <testsuite name="AdLDAP2 Test Suite">
+            <directory suffix="Test.php">./tests/</directory>
+        </testsuite>
+    </testsuites>
+</phpunit>

+ 89 - 0
api/vendor/adldap2/adldap2/readme.md

@@ -0,0 +1,89 @@
+# Adldap2
+
+[![Build Status](https://img.shields.io/travis/Adldap2/Adldap2.svg?style=flat-square)](https://travis-ci.org/Adldap2/Adldap2)
+[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/adLDAP2/adLDAP2/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/adLDAP2/adLDAP2/?branch=master)
+[![Total Downloads](https://img.shields.io/packagist/dt/adldap2/adldap2.svg?style=flat-square)](https://packagist.org/packages/adldap2/adldap2)
+[![Latest Stable Version](https://img.shields.io/packagist/v/adldap2/adldap2.svg?style=flat-square)](https://packagist.org/packages/adldap2/adldap2)
+[![License](https://img.shields.io/packagist/l/adldap2/adldap2.svg?style=flat-square)](https://packagist.org/packages/adldap2/adldap2)
+
+Working with LDAP doesn't need to be hard.
+
+Adldap2 is a tested PHP package that provides LDAP authentication and directory management tools using the Active Record pattern.
+
+## Index
+
+ - [Quick Start](docs/quick-start.md)
+ - [Configuration](docs/configuration.md)
+ - [Connecting](docs/connecting.md)
+ - [Authenticating](docs/authenticating.md)
+ - [Query Builder (Searching)](docs/query-builder.md)
+ - [Models](docs/models/model.md)
+    - [Computer](docs/models/computer.md)
+    - [Contact](docs/models/contact.md)
+    - [Container](docs/models/container.md)
+    - [Entry](docs/models/entry.md)
+    - [Group](docs/models/group.md)
+    - [Organizational Unit](docs/models/ou.md)
+    - [Printer](docs/models/printer.md)
+    - [RootDse](docs/models/root-dse.md)
+    - [User](docs/models/user.md)
+ - [Working with Distinguished Names](docs/distinguished-names.md)
+ - [Schema](docs/schema.md)
+ - [Troubleshooting](docs/troubleshooting.md)
+
+## Installation
+
+### Requirements
+
+To use Adldap2, your server must support:
+
+- PHP 5.5.9 or greater
+- PHP LDAP Extension
+- An LDAP Server
+
+> **Note**: OpenLDAP support is experimental, success may vary.
+
+### Optional Requirements
+
+> **Note: Adldap makes use of `ldap_modify_batch()` for executing modifications to LDAP records**. Your server
+must be on **PHP >= 5.5.10 || >= 5.6.0** to make modifications.
+
+If your AD server requires SSL, your server must support the following libraries:
+
+- PHP SSL Libraries (http://php.net/openssl)
+
+### Installing
+
+Adldap2 utilizes composer for installation.
+
+Run the following command in the root directory of your project:
+
+```
+composer require adldap2/adldap2
+```
+
+> **Note**: If you're upgrading from an earlier release, please take a look
+> at the [release notes](https://github.com/Adldap2/Adldap2/releases).
+
+## Implementations
+
+- [Laravel](https://github.com/Adldap2/Adldap2-Laravel)
+- [Kohana](https://github.com/Adldap2/Adldap2-Kohana)
+
+## Versioning
+
+Adldap2 is versioned under the [Semantic Versioning](http://semver.org/) guidelines as much as possible.
+
+Releases will be numbered with the following format:
+
+`<major>.<minor>.<patch>`
+
+And constructed with the following guidelines:
+
+* Breaking backward compatibility bumps the major and resets the minor and patch.
+* New additions without breaking backward compatibility bumps the minor and resets the patch.
+* Bug fixes and misc changes bumps the patch.
+
+Minor versions are not maintained individually, and you're encouraged to upgrade through to the next minor version.
+
+Major versions are maintained individually through separate branches.

+ 147 - 0
api/vendor/adldap2/adldap2/src/Adldap.php

@@ -0,0 +1,147 @@
+<?php
+
+namespace Adldap;
+
+use InvalidArgumentException;
+use Adldap\Connections\Provider;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Connections\ProviderInterface;
+use Adldap\Connections\ConnectionInterface;
+use Adldap\Configuration\DomainConfiguration;
+
+class Adldap implements AdldapInterface
+{
+    /**
+     * The default provider name.
+     *
+     * @var string
+     */
+    protected $default = 'default';
+
+    /**
+     * The connection providers.
+     *
+     * @var array
+     */
+    protected $providers = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(array $providers = [])
+    {
+        foreach ($providers as $name => $config) {
+            $this->addProvider($config, $name);
+        }
+
+        if ($default = key($providers)) {
+            $this->setDefaultProvider($default);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addProvider($config = [], $name = 'default', ConnectionInterface $connection = null, SchemaInterface $schema = null)
+    {
+        if ($this->isValidConfig($config)) {
+            $config = new Provider($config, $connection, $schema);
+        }
+
+        if ($config instanceof ProviderInterface) {
+            $this->providers[$name] = $config;
+
+            return $this;
+        }
+
+        throw new InvalidArgumentException(
+            "You must provide a configuration array or an instance of Adldap\Connections\ProviderInterface."
+        );
+    }
+
+    /**
+     * Determines if the given config is valid.
+     *
+     * @param mixed $config
+     *
+     * @return bool
+     */
+    protected function isValidConfig($config)
+    {
+        return is_array($config) || $config instanceof DomainConfiguration;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getProviders()
+    {
+        return $this->providers;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getProvider($name)
+    {
+        if (array_key_exists($name, $this->providers)) {
+            return $this->providers[$name];
+        }
+
+        throw new AdldapException("The connection provider '$name' does not exist.");
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setDefaultProvider($name = 'default')
+    {
+        if ($this->getProvider($name) instanceof ProviderInterface) {
+            $this->default = $name;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefaultProvider()
+    {
+        return $this->getProvider($this->default);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function removeProvider($name)
+    {
+        unset($this->providers[$name]);
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect($name = null, $username = null, $password = null)
+    {
+        $provider = $name ? $this->getProvider($name) : $this->getDefaultProvider();
+        
+        return $provider->connect($username, $password);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __call($method, $parameters)
+    {
+        $provider = $this->getDefaultProvider();
+
+        if (!$provider->getConnection()->isBound()) {
+            // We'll make sure we have a bound connection before
+            // allowing dynamic calls on the default provider.
+            $provider->connect();
+        }
+
+        return call_user_func_array([$provider, $method], $parameters);
+    }
+}

+ 8 - 0
api/vendor/adldap2/adldap2/src/AdldapException.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Adldap;
+
+class AdldapException extends \Exception
+{
+    //
+}

+ 93 - 0
api/vendor/adldap2/adldap2/src/AdldapInterface.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Adldap;
+
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Connections\ProviderInterface;
+use Adldap\Connections\ConnectionInterface;
+
+interface AdldapInterface
+{
+    /**
+     * Add a provider by the specified name.
+     *
+     * @param mixed               $configuration
+     * @param string              $name
+     * @param ConnectionInterface $connection
+     * @param SchemaInterface     $schema
+     *
+     * @return $this
+     *
+     * @throws \InvalidArgumentException When an invalid type is given as the configuration argument.
+     */
+    public function addProvider($configuration = [], $name, ConnectionInterface $connection = null, SchemaInterface $schema = null);
+
+    /**
+     * Returns all of the connection providers.
+     *
+     * @return array
+     */
+    public function getProviders();
+
+    /**
+     * Retrieves a Provider using it's specified name.
+     *
+     * @param string $name
+     *
+     * @throws AdldapException When the specified provider does not exist.
+     *
+     * @return ProviderInterface
+     */
+    public function getProvider($name);
+
+    /**
+     * Sets the default provider.
+     *
+     * @param string $name
+     *
+     * @throws AdldapException When the specified provider does not exist.
+     */
+    public function setDefaultProvider($name);
+
+    /**
+     * Retrieves the first default provider.
+     *
+     * @throws AdldapException When no default provider exists.
+     *
+     * @return ProviderInterface
+     */
+    public function getDefaultProvider();
+
+    /**
+     * Removes a provider by the specified name.
+     *
+     * @param string $name
+     *
+     * @return $this
+     */
+    public function removeProvider($name);
+
+    /**
+     * Connects to the specified provider.
+     *
+     * If no username and password is given, then the providers
+     * configured admin credentials are used.
+     *
+     * @param string|null $name
+     * @param string|null $username
+     * @param string|null $password
+     *
+     * @return ProviderInterface
+     */
+    public function connect($name = null, $username = null, $password = null);
+
+    /**
+     * Call methods upon the default provider dynamically.
+     *
+     * @param string $method
+     * @param array  $parameters
+     *
+     * @return mixed
+     */
+    public function __call($method, $parameters);
+}

+ 17 - 0
api/vendor/adldap2/adldap2/src/Auth/BindException.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Adldap\Auth;
+
+use Adldap\AdldapException;
+
+/**
+ * Class BindException
+ *
+ * Thrown when binding to an LDAP connection fails.
+ *
+ * @package Adldap\Auth
+ */
+class BindException extends AdldapException
+{
+    //
+}

+ 145 - 0
api/vendor/adldap2/adldap2/src/Auth/Guard.php

@@ -0,0 +1,145 @@
+<?php
+
+namespace Adldap\Auth;
+
+use Adldap\Connections\ConnectionInterface;
+use Adldap\Configuration\DomainConfiguration;
+
+/**
+ * Class Guard
+ *
+ * Binds users to the current connection.
+ *
+ * @package Adldap\Auth
+ */
+class Guard implements GuardInterface
+{
+    /**
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * @var DomainConfiguration
+     */
+    protected $configuration;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(ConnectionInterface $connection, DomainConfiguration $configuration)
+    {
+        $this->connection = $connection;
+        $this->configuration = $configuration;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function attempt($username, $password, $bindAsUser = false)
+    {
+        $this->validateCredentials($username, $password);
+
+        try {
+            $this->bind($username, $password);
+
+            $result = true;
+        } catch (BindException $e) {
+            // We'll catch the BindException here to allow
+            // developers to use a simple if / else
+            // using the attempt method.
+            $result = false;
+        }
+
+        // If we're not allowed to bind as the user,
+        // we'll rebind as administrator.
+        if ($bindAsUser === false) {
+            // We won't catch any BindException here so we can
+            // catch rebind failures. However this shouldn't
+            // occur if our credentials are correct
+            // in the first place.
+            $this->bindAsAdministrator();
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function bind($username, $password, $prefix = null, $suffix = null)
+    {
+        // We'll allow binding with a null username and password
+        // if they're empty. This will allow us to anonymously
+        // bind to our servers if needed.
+        $username = $username ?: null;
+        $password = $password ?: null;
+
+        if ($username) {
+            $username = $this->applyPrefixAndSuffix($username, $prefix, $suffix);
+        }
+
+        // We'll mute any exceptions / warnings here. All we need to know
+        // is if binding failed and we'll throw our own exception.
+        if (!@$this->connection->bind($username, $password)) {
+            throw new BindException($this->connection->getLastError(), $this->connection->errNo());
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function bindAsAdministrator()
+    {
+        $this->bind(
+            $this->configuration->get('admin_username'),
+            $this->configuration->get('admin_password'),
+            $this->configuration->get('admin_account_prefix'),
+            $this->configuration->get('admin_account_suffix')
+        );
+    }
+
+    /**
+     * Applies the prefix and suffix to the given username.
+     *
+     * Applies the configured account prefix and suffix if they are null.
+     *
+     * @param string      $username
+     * @param string|null $prefix
+     * @param string|null $suffix
+     *
+     * @return string
+     *
+     * @throws \Adldap\Configuration\ConfigurationException If account_suffix or account_prefix do not
+     *                                                      exist in the providers domain configuration
+     */
+    protected function applyPrefixAndSuffix($username, $prefix = null, $suffix = null)
+    {
+        $prefix = is_null($prefix) ? $this->configuration->get('account_prefix') : $prefix;
+        $suffix = is_null($suffix) ? $this->configuration->get('account_suffix') : $suffix;
+
+        return $prefix.$username.$suffix;
+    }
+
+    /**
+     * Validates the specified username and password from being empty.
+     *
+     * @param string $username
+     * @param string $password
+     *
+     * @throws PasswordRequiredException When the given password is empty.
+     * @throws UsernameRequiredException When the given username is empty.
+     */
+    protected function validateCredentials($username, $password)
+    {
+        if (empty($username)) {
+            // Check for an empty username.
+            throw new UsernameRequiredException('A username must be specified.');
+        }
+
+        if (empty($password)) {
+            // Check for an empty password.
+            throw new PasswordRequiredException('A password must be specified.');
+        }
+    }
+}

+ 56 - 0
api/vendor/adldap2/adldap2/src/Auth/GuardInterface.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace Adldap\Auth;
+
+use Adldap\Connections\ConnectionInterface;
+use Adldap\Configuration\DomainConfiguration;
+
+interface GuardInterface
+{
+    /**
+     * Constructor.
+     *
+     * @param ConnectionInterface $connection
+     * @param DomainConfiguration $configuration
+     */
+    public function __construct(ConnectionInterface $connection, DomainConfiguration $configuration);
+
+    /**
+     * Authenticates a user using the specified credentials.
+     *
+     * @param string $username   The users AD username.
+     * @param string $password   The users AD password.
+     * @param bool   $bindAsUser Whether or not to bind as the user.
+     *
+     * @throws \Adldap\Auth\BindException When re-binding to your LDAP server fails.
+     * @throws \Adldap\Auth\UsernameRequiredException When username is empty.
+     * @throws \Adldap\Auth\PasswordRequiredException When password is empty.
+     *
+     * @return bool
+     */
+    public function attempt($username, $password, $bindAsUser = false);
+
+    /**
+     * Binds to the current connection using the inserted credentials.
+     *
+     * @param string $username
+     * @param string $password
+     * @param string $prefix
+     * @param string $suffix
+     *
+     * @throws \Adldap\Auth\BindException When binding to your LDAP server fails.
+     *
+     * @return void
+     */
+    public function bind($username, $password, $prefix = null, $suffix = null);
+
+    /**
+     * Binds to the current LDAP server using the
+     * configuration administrator credentials.
+     *
+     * @throws \Adldap\Auth\BindException When binding as your administrator account fails.
+     *
+     * @return void
+     */
+    public function bindAsAdministrator();
+}

+ 10 - 0
api/vendor/adldap2/adldap2/src/Auth/PasswordRequiredException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Adldap\Auth;
+
+use Adldap\AdldapException;
+
+class PasswordRequiredException extends AdldapException
+{
+    //
+}

+ 10 - 0
api/vendor/adldap2/adldap2/src/Auth/UsernameRequiredException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Adldap\Auth;
+
+use Adldap\AdldapException;
+
+class UsernameRequiredException extends AdldapException
+{
+    //
+}

+ 18 - 0
api/vendor/adldap2/adldap2/src/Configuration/ConfigurationException.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Adldap\Configuration;
+
+use Adldap\AdldapException;
+
+/**
+ * Class ConfigurationException
+ *
+ * Thrown when a configuration value does not exist, or a
+ * configuration value being set is not valid.
+ *
+ * @package Adldap\Configuration
+ */
+class ConfigurationException extends AdldapException
+{
+    //
+}

+ 167 - 0
api/vendor/adldap2/adldap2/src/Configuration/DomainConfiguration.php

@@ -0,0 +1,167 @@
+<?php
+
+namespace Adldap\Configuration;
+
+use Adldap\Connections\ConnectionInterface;
+use Adldap\Configuration\Validators\ArrayValidator;
+use Adldap\Configuration\Validators\StringOrNullValidator;
+use Adldap\Configuration\Validators\BooleanValidator;
+use Adldap\Configuration\Validators\IntegerValidator;
+
+/**
+ * Class DomainConfiguration
+ *
+ * Contains an array of configuration options for a single LDAP connection.
+ *
+ * @package Adldap\Configuration
+ */
+class DomainConfiguration
+{
+    /**
+     * The configuration options array.
+     *
+     * The default values for each key indicate the type of value it requires.
+     *
+     * @var array
+     */
+    protected $options = [
+        // An array of LDAP hosts.
+        'domain_controllers' => [],
+
+        // The global LDAP operation timeout limit in seconds.
+        'timeout' => 5,
+
+        // The LDAP version to utilize.
+        'version' => 3,
+
+        // The port to use for connecting to your hosts.
+        'port' => ConnectionInterface::PORT,
+
+        // The base distinguished name of your domain.
+        'base_dn' => '',
+
+        // Whether or not to use SSL when connecting to your hosts.
+        'use_ssl' => false,
+
+        // Whether or not to use TLS when connecting to your hosts.
+        'use_tls' => false,
+
+        // Whether or not follow referrals is enabled when performing LDAP operations.
+        'follow_referrals' => false,
+
+        // The account prefix to use when authenticating users.
+        'account_prefix' => null,
+
+        // The account suffix to use when authenticating users.
+        'account_suffix' => null,
+
+        // The username to connect to your hosts with.
+        'admin_username' => '',
+
+        // The password that is utilized with the above user.
+        'admin_password' => '',
+
+        // The account prefix to use when authenticating your admin account above.
+        'admin_account_prefix' => null,
+
+        // The account prefix to use when authenticating your admin account above.
+        'admin_account_suffix' => null,
+
+        // Custom LDAP options that you'd like to utilize.
+        'custom_options' => [],
+    ];
+
+    /**
+     * Constructor.
+     *
+     * @param array $options
+     *
+     * @throws ConfigurationException When an option value given is an invalid type.
+     */
+    public function __construct(array $options = [])
+    {
+        foreach ($options as $key => $value) {
+            $this->set($key, $value);
+        }
+    }
+
+    /**
+     * Sets a configuration option.
+     *
+     * Throws an exception if the specified option does
+     * not exist, or if it's an invalid type.
+     *
+     * @param string $key
+     * @param mixed  $value
+     *
+     * @throws ConfigurationException When an option value given is an invalid type.
+     */
+    public function set($key, $value)
+    {
+        if($this->validate($key, $value)) {
+            $this->options[$key] = $value;
+        }
+    }
+
+    /**
+     * Returns the value for the specified configuration options.
+     *
+     * Throws an exception if the specified option does not exist.
+     *
+     * @param string $key
+     *
+     * @return mixed
+     *
+     * @throws ConfigurationException When the option specified does not exist.
+     */
+    public function get($key)
+    {
+        if ($this->has($key)) {
+            return $this->options[$key];
+        }
+
+        throw new ConfigurationException("Option {$key} does not exist.");
+    }
+
+    /**
+     * Checks if a configuration option exists.
+     *
+     * @param string $key
+     *
+     * @return bool
+     */
+    public function has($key)
+    {
+        return array_key_exists($key, $this->options);
+    }
+
+    /**
+     * Validates the new configuration option against its
+     * default value to ensure it's the correct type.
+     *
+     * If an invalid type is given, an exception is thrown.
+     *
+     * @param string $key
+     * @param mixed  $value
+     *
+     * @return bool
+     *
+     * @throws ConfigurationException When an option value given is an invalid type.
+     */
+    protected function validate($key, $value)
+    {
+        $default = $this->get($key);
+
+        if (is_array($default)) {
+            $validator = new ArrayValidator($key, $value);
+        } elseif (is_int($default)) {
+            $validator = new IntegerValidator($key, $value);
+        } elseif (is_bool($default)) {
+            $validator = new BooleanValidator($key, $value);
+        } else {
+            $validator = new StringOrNullValidator($key, $value);
+        }
+
+        return $validator->validate();
+    }
+}

+ 27 - 0
api/vendor/adldap2/adldap2/src/Configuration/Validators/ArrayValidator.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Adldap\Configuration\Validators;
+
+use Adldap\Configuration\ConfigurationException;
+
+/**
+ * Class ArrayValidator
+ *
+ * Validates that the configuration value is an array.
+ *
+ * @package Adldap\Configuration\Validators
+ */
+class ArrayValidator extends Validator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function validate()
+    {
+        if (!is_array($this->value)) {
+            throw new ConfigurationException("Option {$this->key} must be an array.");
+        }
+
+        return true;
+    }
+}

+ 27 - 0
api/vendor/adldap2/adldap2/src/Configuration/Validators/BooleanValidator.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Adldap\Configuration\Validators;
+
+use Adldap\Configuration\ConfigurationException;
+
+/**
+ * Class BooleanValidator
+ *
+ * Validates that the configuration value is a boolean.
+ *
+ * @package Adldap\Configuration\Validators
+ */
+class BooleanValidator extends Validator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function validate()
+    {
+        if (!is_bool($this->value)) {
+            throw new ConfigurationException("Option {$this->key} must be a boolean.");
+        }
+
+        return true;
+    }
+}

+ 27 - 0
api/vendor/adldap2/adldap2/src/Configuration/Validators/IntegerValidator.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Adldap\Configuration\Validators;
+
+use Adldap\Configuration\ConfigurationException;
+
+/**
+ * Class IntegerValidator
+ *
+ * Validates that the configuration value is an integer / number.
+ *
+ * @package Adldap\Configuration\Validators
+ */
+class IntegerValidator extends Validator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function validate()
+    {
+        if (!is_numeric($this->value)) {
+            throw new ConfigurationException("Option {$this->key} must be an integer.");
+        }
+
+        return true;
+    }
+}

+ 27 - 0
api/vendor/adldap2/adldap2/src/Configuration/Validators/StringOrNullValidator.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Adldap\Configuration\Validators;
+
+use Adldap\Configuration\ConfigurationException;
+
+/**
+ * Class StringOrNullValidator
+ *
+ * Validates that the configuration value is a string or null.
+ *
+ * @package Adldap\Configuration\Validators
+ */
+class StringOrNullValidator extends Validator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function validate()
+    {
+        if (is_string($this->value) || is_null($this->value)) {
+            return true;
+        }
+
+        throw new ConfigurationException("Option {$this->key} must be a string or null.");
+    }
+}

+ 48 - 0
api/vendor/adldap2/adldap2/src/Configuration/Validators/Validator.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Adldap\Configuration\Validators;
+
+/**
+ * Class Validator
+ *
+ * Validates configuration values.
+ *
+ * @package Adldap\Configuration\Validators
+ */
+abstract class Validator
+{
+    /**
+     * The configuration key under validation.
+     *
+     * @var string
+     */
+    protected $key;
+
+    /**
+     * The configuration value under validation.
+     *
+     * @var mixed
+     */
+    protected $value;
+
+    /**
+     * Constructor.
+     *
+     * @param string $key
+     * @param mixed  $value
+     */
+    public function __construct($key, $value)
+    {
+        $this->key = $key;
+        $this->value = $value;
+    }
+
+    /**
+     * Validates the configuration value.
+     *
+     * @return bool
+     *
+     * @throws \Adldap\Configuration\ConfigurationException When the value given fails validation.
+     */
+    abstract public function validate();
+}

+ 10 - 0
api/vendor/adldap2/adldap2/src/Connections/ConnectionException.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Adldap\Connections;
+
+use Adldap\AdldapException;
+
+class ConnectionException extends AdldapException
+{
+    //
+}

+ 481 - 0
api/vendor/adldap2/adldap2/src/Connections/ConnectionInterface.php

@@ -0,0 +1,481 @@
+<?php
+
+namespace Adldap\Connections;
+
+/**
+ * The Connection interface used for making connections. Implementing
+ * this interface on connection classes helps unit and functional
+ * test classes that require a connection.
+ *
+ * Interface ConnectionInterface
+ */
+interface ConnectionInterface
+{
+    /**
+     * The SSL LDAP protocol string.
+     *
+     * @var string
+     */
+    const PROTOCOL_SSL = 'ldaps://';
+
+    /**
+     * The standard LDAP protocol string.
+     *
+     * @var string
+     */
+    const PROTOCOL = 'ldap://';
+
+    /**
+     * The LDAP SSL port number.
+     *
+     * @var string
+     */
+    const PORT_SSL = 636;
+
+    /**
+     * The standard LDAP port number.
+     *
+     * @var string
+     */
+    const PORT = 389;
+
+    /**
+     * Returns true / false if the
+     * current connection is supported
+     * on the current PHP install.
+     *
+     * @return bool
+     */
+    public function isSupported();
+
+    /**
+     * Returns true / false if the
+     * current connection supports
+     * SASL for single sign on
+     * capability.
+     *
+     * @return bool
+     */
+    public function isSaslSupported();
+
+    /**
+     * Returns true / false if the
+     * current connection pagination.
+     *
+     * @return bool
+     */
+    public function isPagingSupported();
+
+    /**
+     * Returns true / false if the
+     * current connection supports batch
+     * modification.
+     *
+     * @return bool
+     */
+    public function isBatchSupported();
+
+    /**
+     * Returns true / false if the
+     * current connection instance is using
+     * SSL.
+     *
+     * @return bool
+     */
+    public function isUsingSSL();
+
+    /**
+     * Returns true / false if the
+     * current connection instance is using
+     * TLS.
+     *
+     * @return bool
+     */
+    public function isUsingTLS();
+
+    /**
+     * Returns true / false if the current
+     * connection is able to modify passwords.
+     *
+     * @return bool
+     */
+    public function canChangePasswords();
+
+    /**
+     * Returns true / false if the current
+     * connection is bound.
+     *
+     * @return bool
+     */
+    public function isBound();
+
+    /**
+     * Sets the current connection to use SSL.
+     *
+     * @param bool $enabled
+     *
+     * @return ConnectionInterface
+     */
+    public function ssl($enabled = true);
+
+    /**
+     * Sets the current connection to use TLS.
+     *
+     * @param bool $enabled
+     *
+     * @return ConnectionInterface
+     */
+    public function tls($enabled = true);
+
+    /**
+     * Get the current connection.
+     *
+     * @return mixed
+     */
+    public function getConnection();
+
+    /**
+     * Retrieve the entries from a search result.
+     *
+     * @param $searchResult
+     *
+     * @return mixed
+     */
+    public function getEntries($searchResult);
+
+    /**
+     * Returns the number of entries from a search
+     * result.
+     *
+     * @param $searchResult
+     *
+     * @return int
+     */
+    public function countEntries($searchResult);
+
+    /**
+     * Compare value of attribute found in entry specified with DN.
+     *
+     * @param string $dn
+     * @param string $attribute
+     * @param string $value
+     *
+     * @return mixed
+     */
+    public function compare($dn, $attribute, $value);
+
+    /**
+     * Retrieves the first entry from a search result.
+     *
+     * @param $searchResult
+     *
+     * @return mixed
+     */
+    public function getFirstEntry($searchResult);
+
+    /**
+     * Retrieves the next entry from a search result.
+     *
+     * @param $entry
+     *
+     * @return mixed
+     */
+    public function getNextEntry($entry);
+
+    /**
+     * Retrieves the ldap entry's attributes.
+     *
+     * @param $entry
+     *
+     * @return mixed
+     */
+    public function getAttributes($entry);
+
+    /**
+     * Retrieve the last error on the current
+     * connection.
+     *
+     * @return string
+     */
+    public function getLastError();
+
+    /**
+     * Get all binary values from the specified result entry.
+     *
+     * @param $entry
+     * @param $attribute
+     *
+     * @return array
+     */
+    public function getValuesLen($entry, $attribute);
+
+    /**
+     * Sets an option on the current connection.
+     *
+     * @param int   $option
+     * @param mixed $value
+     *
+     * @return mixed
+     */
+    public function setOption($option, $value);
+
+    /**
+     * Sets options on the current connection.
+     *
+     * @param array $options
+     *
+     * @return mixed
+     */
+    public function setOptions(array $options = []);
+
+    /**
+     * Set a callback function to do re-binds on referral chasing.
+     *
+     * @param callable $callback
+     *
+     * @return bool
+     */
+    public function setRebindCallback(callable $callback);
+
+    /**
+     * Connects to the specified hostname using the specified port.
+     *
+     * @param string|array $hostname
+     * @param int          $port
+     *
+     * @return mixed
+     */
+    public function connect($hostname = [], $port = 389);
+
+    /**
+     * Starts a connection using TLS.
+     *
+     * @return mixed
+     */
+    public function startTLS();
+
+    /**
+     * Binds to the current connection using
+     * the specified username and password. If sasl
+     * is true, the current connection is bound using
+     * SASL.
+     *
+     * @param string $username
+     * @param string $password
+     * @param bool   $sasl
+     *
+     * @return bool
+     */
+    public function bind($username, $password, $sasl = false);
+
+    /**
+     * Closes the current connection.
+     *
+     * Returns false if no connection is present.
+     *
+     * @return bool
+     */
+    public function close();
+
+    /**
+     * Performs a search on the current connection.
+     *
+     * @param string $dn
+     * @param string $filter
+     * @param array  $fields
+     * @param bool   $onlyAttributes
+     * @param int    $size
+     * @param int    $time
+     *
+     * @return mixed
+     */
+    public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0);
+
+    /**
+     * Reads an entry on the current connection.
+     *
+     * @param string $dn
+     * @param $filter
+     * @param array $fields
+     * @param bool   $onlyAttributes
+     * @param int    $size
+     * @param int    $time
+     *
+     * @return mixed
+     */
+    public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0);
+
+    /**
+     * Performs a single level search on the current connection.
+     *
+     * @param string $dn
+     * @param string $filter
+     * @param array  $attributes
+     * @param bool   $onlyAttributes
+     * @param int    $size
+     * @param int    $time
+     *
+     * @return mixed
+     */
+    public function listing($dn, $filter, array $attributes, $onlyAttributes = false, $size = 0, $time = 0);
+
+    /**
+     * Adds an entry to the current connection.
+     *
+     * @param string $dn
+     * @param array  $entry
+     *
+     * @return bool
+     */
+    public function add($dn, array $entry);
+
+    /**
+     * Deletes an entry on the current connection.
+     *
+     * @param string $dn
+     *
+     * @return bool
+     */
+    public function delete($dn);
+
+    /**
+     * Modify the name of an entry on the current
+     * connection.
+     *
+     * @param string $dn
+     * @param string $newRdn
+     * @param string $newParent
+     * @param bool   $deleteOldRdn
+     *
+     * @return bool
+     */
+    public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false);
+
+    /**
+     * Modifies an existing entry on the
+     * current connection.
+     *
+     * @param string $dn
+     * @param array  $entry
+     *
+     * @return bool
+     */
+    public function modify($dn, array $entry);
+
+    /**
+     * Batch modifies an existing entry on the
+     * current connection.
+     *
+     * @param string $dn
+     * @param array  $values
+     *
+     * @return mixed
+     */
+    public function modifyBatch($dn, array $values);
+
+    /**
+     * Add attribute values to current attributes.
+     *
+     * @param string $dn
+     * @param array  $entry
+     *
+     * @return mixed
+     */
+    public function modAdd($dn, array $entry);
+
+    /**
+     * Replaces attribute values with new ones.
+     *
+     * @param string $dn
+     * @param array  $entry
+     *
+     * @return mixed
+     */
+    public function modReplace($dn, array $entry);
+
+    /**
+     * Delete attribute values from current attributes.
+     *
+     * @param string $dn
+     * @param array  $entry
+     *
+     * @return mixed
+     */
+    public function modDelete($dn, array $entry);
+
+    /**
+     * Send LDAP pagination control.
+     *
+     * @param int    $pageSize
+     * @param bool   $isCritical
+     * @param string $cookie
+     *
+     * @return mixed
+     */
+    public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '');
+
+    /**
+     * Retrieve a paginated result response.
+     *
+     * @param $result
+     * @param string $cookie
+     *
+     * @return mixed
+     */
+    public function controlPagedResultResponse($result, &$cookie);
+
+    /**
+     * Returns the error number of the last command
+     * executed on the current connection.
+     *
+     * @return int
+     */
+    public function errNo();
+
+    /**
+     * Returns the extended error string of the last command.
+     *
+     * @return string
+     */
+    public function getExtendedError();
+
+    /**
+     * Returns the extended error hex code of the last command.
+     *
+     * @return string|null
+     */
+    public function getExtendedErrorHex();
+
+    /**
+     * Returns the extended error code of the last command.
+     *
+     * @return string
+     */
+    public function getExtendedErrorCode();
+
+    /**
+     * Returns the error string of the specified
+     * error number.
+     *
+     * @param int $number
+     *
+     * @return string
+     */
+    public function err2Str($number);
+
+    /**
+     * Return the diagnostic Message.
+     *
+     * @return string
+     */
+    public function getDiagnosticMessage();
+
+    /**
+     * Extract the diagnostic code from the message.
+     *
+     * @param string $message
+     *
+     * @return string|bool
+     */
+    public function extractDiagnosticCode($message);
+}

+ 456 - 0
api/vendor/adldap2/adldap2/src/Connections/Ldap.php

@@ -0,0 +1,456 @@
+<?php
+
+namespace Adldap\Connections;
+
+use Adldap\AdldapException;
+
+/**
+ * Class Ldap
+ *
+ * A class that abstracts PHP's LDAP functions and stores the bound connection.
+ *
+ * @package Adldap\Connections
+ */
+class Ldap implements ConnectionInterface
+{
+    use LdapFunctionSupportTrait;
+
+    /**
+     * The active LDAP connection.
+     *
+     * @var resource
+     */
+    protected $connection;
+
+    /**
+     * Stores the bool whether or not
+     * the current connection is bound.
+     *
+     * @var bool
+     */
+    protected $bound = false;
+
+    /**
+     * Stores the bool to tell the connection
+     * whether or not to use SSL.
+     *
+     * To use SSL, your server must support LDAP over SSL.
+     * http://adldap.sourceforge.net/wiki/doku.php?id=ldap_over_ssl
+     *
+     * @var bool
+     */
+    protected $useSSL = false;
+
+    /**
+     * Stores the bool to tell the connection
+     * whether or not to use TLS.
+     *
+     * If you wish to use TLS you should ensure that $useSSL is set to false and vice-versa
+     *
+     * @var bool
+     */
+    protected $useTLS = false;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isUsingSSL()
+    {
+        return $this->useSSL;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isUsingTLS()
+    {
+        return $this->useTLS;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isBound()
+    {
+        return $this->bound;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function canChangePasswords()
+    {
+        return $this->isUsingSSL() || $this->isUsingTLS();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function ssl($enabled = true)
+    {
+        $this->useSSL = $enabled;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function tls($enabled = true)
+    {
+        $this->useTLS = $enabled;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getEntries($searchResults)
+    {
+        return ldap_get_entries($this->getConnection(), $searchResults);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getFirstEntry($searchResults)
+    {
+        return ldap_first_entry($this->getConnection(), $searchResults);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getNextEntry($entry)
+    {
+        return ldap_next_entry($this->getConnection(), $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributes($entry)
+    {
+        return ldap_get_attributes($this->getConnection(), $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function countEntries($searchResults)
+    {
+        return ldap_count_entries($this->getConnection(), $searchResults);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function compare($dn, $attribute, $value)
+    {
+        return ldap_compare($this->getConnection(), $dn, $attribute, $value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getLastError()
+    {
+        return ldap_error($this->getConnection());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getValuesLen($entry, $attribute)
+    {
+        return ldap_get_values_len($this->getConnection(), $entry, $attribute);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOption($option, $value)
+    {
+        return ldap_set_option($this->getConnection(), $option, $value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setOptions(array $options = [])
+    {
+        foreach ($options as $option => $value) {
+            $this->setOption($option, $value);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setRebindCallback(callable $callback)
+    {
+        return ldap_set_rebind_proc($this->getConnection(), $callback);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function startTLS()
+    {
+        return ldap_start_tls($this->getConnection());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect($hosts = [], $port = '389')
+    {
+        $connections = $this->getConnectionString($hosts, $this->getProtocol(), $port);
+
+        return $this->connection = ldap_connect($connections);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function close()
+    {
+        $connection = $this->getConnection();
+
+        return is_resource($connection) ? ldap_close($connection) : false;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
+    {
+        return ldap_search($this->getConnection(), $dn, $filter, $fields, $onlyAttributes, $size, $time);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
+    {
+        return ldap_list($this->getConnection(), $dn, $filter, $fields, $onlyAttributes, $size, $time);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0)
+    {
+        return ldap_read($this->getConnection(), $dn, $filter, $fields, $onlyAttributes, $size, $time);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function bind($username, $password, $sasl = false)
+    {
+        if ($this->isUsingTLS()) {
+            $this->startTLS();
+        }
+
+        if ($sasl) {
+            return $this->bound = ldap_sasl_bind($this->getConnection(), null, null, 'GSSAPI');
+        }
+
+        return $this->bound = ldap_bind($this->getConnection(), $username, $password);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function add($dn, array $entry)
+    {
+        return ldap_add($this->getConnection(), $dn, $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete($dn)
+    {
+        return ldap_delete($this->getConnection(), $dn);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false)
+    {
+        return ldap_rename($this->getConnection(), $dn, $newRdn, $newParent, $deleteOldRdn);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modify($dn, array $entry)
+    {
+        return ldap_modify($this->getConnection(), $dn, $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modifyBatch($dn, array $values)
+    {
+        return ldap_modify_batch($this->getConnection(), $dn, $values);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modAdd($dn, array $entry)
+    {
+        return ldap_mod_add($this->getConnection(), $dn, $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modReplace($dn, array $entry)
+    {
+        return ldap_mod_replace($this->getConnection(), $dn, $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function modDelete($dn, array $entry)
+    {
+        return ldap_mod_del($this->getConnection(), $dn, $entry);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '')
+    {
+        if ($this->isPagingSupported()) {
+            return ldap_control_paged_result($this->getConnection(), $pageSize, $isCritical, $cookie);
+        }
+
+        throw new AdldapException(
+            'LDAP Pagination is not supported on your current PHP installation.'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function controlPagedResultResponse($result, &$cookie)
+    {
+        if ($this->isPagingSupported()) {
+            return ldap_control_paged_result_response($this->getConnection(), $result, $cookie);
+        }
+
+        throw new AdldapException(
+            'LDAP Pagination is not supported on your current PHP installation.'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function errNo()
+    {
+        return ldap_errno($this->getConnection());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtendedError()
+    {
+        return $this->getDiagnosticMessage();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtendedErrorHex()
+    {
+        if (preg_match("/(?<=data\s).*?(?=\,)/", $this->getExtendedError(), $code)) {
+            return $code[0];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtendedErrorCode()
+    {
+        return $this->extractDiagnosticCode($this->getExtendedError());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function err2Str($number)
+    {
+        return ldap_err2str($number);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDiagnosticMessage()
+    {
+        ldap_get_option($this->getConnection(), LDAP_OPT_ERROR_STRING, $diagnosticMessage);
+
+        return $diagnosticMessage;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function extractDiagnosticCode($message)
+    {
+        preg_match('/^([\da-fA-F]+):/', $message, $matches);
+
+        return isset($matches[1]) ? $matches[1] : false;
+    }
+
+    /**
+     * Returns the LDAP protocol to utilize for the current connection.
+     *
+     * @return string
+     */
+    public function getProtocol()
+    {
+        return $this->isUsingSSL() ? $this::PROTOCOL_SSL : $this::PROTOCOL;
+    }
+
+    /**
+     * Generates an LDAP connection string for each host given.
+     *
+     * @param string|array  $hosts
+     * @param string        $protocol
+     * @param string        $port
+     *
+     * @return string
+     */
+    protected function getConnectionString($hosts = [], $protocol, $port)
+    {
+        // Normalize hosts into an array.
+        $hosts = is_array($hosts) ? $hosts : [$hosts];
+
+        $hosts = array_map(function ($host) use ($protocol, $port) {
+            return "{$protocol}{$host}:{$port}";
+        }, $hosts);
+
+        return implode(' ', $hosts);
+    }
+}

+ 52 - 0
api/vendor/adldap2/adldap2/src/Connections/LdapFunctionSupportTrait.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Adldap\Connections;
+
+trait LdapFunctionSupportTrait
+{
+    /**
+     * Returns true / false if the current
+     * PHP install supports LDAP.
+     *
+     * @return bool
+     */
+    public function isSupported()
+    {
+        return function_exists('ldap_connect');
+    }
+
+    /**
+     * Returns true / false if the current
+     * PHP install supports an SASL bound
+     * LDAP connection.
+     *
+     * @return bool
+     */
+    public function isSaslSupported()
+    {
+        return function_exists('ldap_sasl_bind');
+    }
+
+    /**
+     * Returns true / false if the current
+     * PHP install supports LDAP paging.
+     *
+     * @return bool
+     */
+    public function isPagingSupported()
+    {
+        return function_exists('ldap_control_paged_result');
+    }
+
+    /**
+     * Returns true / false if the current
+     * PHP install supports batch modification.
+     * Requires PHP 5.4 >= 5.4.26, PHP 5.5 >= 5.5.10 or PHP 5.6 >= 5.6.0.
+     *
+     * @return bool
+     */
+    public function isBatchSupported()
+    {
+        return function_exists('ldap_modify_batch');
+    }
+}

+ 260 - 0
api/vendor/adldap2/adldap2/src/Connections/Provider.php

@@ -0,0 +1,260 @@
+<?php
+
+namespace Adldap\Connections;
+
+use InvalidArgumentException;
+use Adldap\Auth\Guard;
+use Adldap\Auth\GuardInterface;
+use Adldap\Schemas\ActiveDirectory;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Models\Factory as ModelFactory;
+use Adldap\Query\Factory as SearchFactory;
+use Adldap\Configuration\DomainConfiguration;
+
+/**
+ * Class Provider
+ *
+ * Contains the LPAP connection and domain configuration to
+ * instantiate factories for retrieving and creating
+ * LDAP records as well as authentication (binding).
+ *
+ * @package Adldap\Connections
+ */
+class Provider implements ProviderInterface
+{
+    /**
+     * The providers connection.
+     *
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * The providers configuration.
+     *
+     * @var DomainConfiguration
+     */
+    protected $configuration;
+
+    /**
+     * The providers schema.
+     *
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * The providers auth guard instance.
+     *
+     * @var GuardInterface
+     */
+    protected $guard;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct($configuration = [], ConnectionInterface $connection = null, SchemaInterface $schema = null)
+    {
+        $this->setConfiguration($configuration)
+            ->setConnection($connection)
+            ->setSchema($schema);
+    }
+
+    /**
+     * Close the LDAP connection (if bound) upon destruction.
+     * 
+     * @return void
+     */
+    public function __destruct()
+    {
+        if (
+            $this->connection instanceof ConnectionInterface &&
+            $this->connection->isBound()
+        ) {
+            $this->connection->close();
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setConfiguration($configuration = [])
+    {
+        if (is_array($configuration)) {
+            $configuration = new DomainConfiguration($configuration);
+        }
+
+        if ($configuration instanceof DomainConfiguration) {
+            $this->configuration = $configuration;
+            
+            return $this;
+        }
+
+        $class = DomainConfiguration::class;
+        
+        throw new InvalidArgumentException(
+            "Configuration must be array or instance of $class"
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setConnection(ConnectionInterface $connection = null)
+    {
+        // We'll create a standard connection if one isn't given.
+        $this->connection = $connection ?: new Ldap();
+
+        // Prepare the connection.
+        $this->prepareConnection();
+
+        // Instantiate the LDAP connection.
+        $this->connection->connect(
+            $this->configuration->get('domain_controllers'),
+            $this->configuration->get('port')
+        );
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSchema(SchemaInterface $schema = null)
+    {
+        $this->schema = $schema ?: new ActiveDirectory();
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setGuard(GuardInterface $guard)
+    {
+        $this->guard = $guard;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfiguration()
+    {
+        return $this->configuration;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSchema()
+    {
+        return $this->schema;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getGuard()
+    {
+        if (!$this->guard instanceof GuardInterface) {
+            $this->setGuard($this->getDefaultGuard($this->connection, $this->configuration));
+        }
+
+        return $this->guard;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefaultGuard(ConnectionInterface $connection, DomainConfiguration $configuration)
+    {
+        return new Guard($connection, $configuration);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function make()
+    {
+        return new ModelFactory(
+            $this->search()->getQuery()
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function search()
+    {
+        return new SearchFactory(
+            $this->connection,
+            $this->schema,
+            $this->configuration->get('base_dn')
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function auth()
+    {
+        return $this->getGuard();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function connect($username = null, $password = null)
+    {
+        // Get the default guard instance.
+        $guard = $this->getGuard();
+
+        if (is_null($username) && is_null($password)) {
+            // If both the username and password are null, we'll connect to the server
+            // using the configured administrator username and password.
+            $guard->bindAsAdministrator();
+        } else {
+            // Bind to the server with the specified username and password otherwise.
+            $guard->bind($username, $password);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Prepares the connection by setting configured parameters.
+     *
+     * @return void
+     *
+     * @throws \Adldap\Configuration\ConfigurationException When configuration options requested do not exist
+     */
+    protected function prepareConnection()
+    {
+        if ($this->configuration->get('use_ssl')) {
+            $this->connection->ssl();
+        } elseif ($this->configuration->get('use_tls')) {
+            $this->connection->tls();
+        }
+
+        $options = array_replace(
+            $this->configuration->get('custom_options'),
+            [
+                LDAP_OPT_PROTOCOL_VERSION => $this->configuration->get('version'),
+                LDAP_OPT_NETWORK_TIMEOUT => $this->configuration->get('timeout'),
+                LDAP_OPT_REFERRALS => $this->configuration->get('follow_referrals')
+            ]
+        );
+
+        $this->connection->setOptions($options);
+    }
+}

+ 127 - 0
api/vendor/adldap2/adldap2/src/Connections/ProviderInterface.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace Adldap\Connections;
+
+use Adldap\Auth\GuardInterface;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Configuration\DomainConfiguration;
+
+interface ProviderInterface
+{
+    /**
+      * Constructor.
+      *
+      * @param DomainConfiguration|array $configuration
+      * @param ConnectionInterface|null  $connection
+      * @param SchemaInterface|null      $schema
+      */
+    public function __construct($configuration, ConnectionInterface $connection, SchemaInterface $schema = null);
+
+    /**
+     * Returns the current connection instance.
+     *
+     * @return ConnectionInterface
+     */
+    public function getConnection();
+
+    /**
+     * Returns the current configuration instance.
+     *
+     * @return DomainConfiguration
+     */
+    public function getConfiguration();
+
+    /**
+     * Returns the current Guard instance.
+     *
+     * @return \Adldap\Auth\Guard
+     */
+    public function getGuard();
+
+    /**
+     * Returns a new default Guard instance.
+     *
+     * @param ConnectionInterface $connection
+     * @param DomainConfiguration $configuration
+     *
+     * @return \Adldap\Auth\Guard
+     */
+    public function getDefaultGuard(ConnectionInterface $connection, DomainConfiguration $configuration);
+
+    /**
+     * Sets the current connection.
+     *
+     * @param ConnectionInterface $connection
+     *
+     * @return $this
+     */
+    public function setConnection(ConnectionInterface $connection = null);
+
+    /**
+     * Sets the current configuration.
+     *
+     * @param DomainConfiguration|array $configuration
+     */
+    public function setConfiguration($configuration = []);
+
+    /**
+     * Sets the current LDAP attribute schema.
+     *
+     * @param SchemaInterface|null $schema
+     *
+     * @return $this
+     */
+    public function setSchema(SchemaInterface $schema = null);
+
+    /**
+     * Returns the current LDAP attribute schema.
+     *
+     * @return SchemaInterface
+     */
+    public function getSchema();
+
+    /**
+     * Sets the current Guard instance.
+     *
+     * @param GuardInterface $guard
+     *
+     * @return $this
+     */
+    public function setGuard(GuardInterface $guard);
+
+    /**
+     * Returns a new Model factory instance.
+     *
+     * @return \Adldap\Models\Factory
+     */
+    public function make();
+
+    /**
+     * Returns a new Search factory instance.
+     *
+     * @return \Adldap\Query\Factory
+     */
+    public function search();
+
+    /**
+     * Returns a new Auth Guard instance.
+     *
+     * @return \Adldap\Auth\Guard
+     */
+    public function auth();
+
+    /**
+     * Connects and Binds to the Domain Controller.
+     *
+     * If no username or password is specified, then the
+     * configured administrator credentials are used.
+     *
+     * @param string|null $username
+     * @param string|null $password
+     *
+     * @throws \Adldap\Auth\BindException When binding to your LDAP server fails.
+     *
+     * @return ProviderInterface
+     */
+    public function connect($username = null, $password = null);
+}

+ 412 - 0
api/vendor/adldap2/adldap2/src/Models/Attributes/AccountControl.php

@@ -0,0 +1,412 @@
+<?php
+
+namespace Adldap\Models\Attributes;
+
+/**
+ * The Account Control class.
+ *
+ * This class is for easily building a user account control value.
+ *
+ * @link https://support.microsoft.com/en-us/kb/305144
+ */
+class AccountControl
+{
+    const SCRIPT = 1;
+
+    const ACCOUNTDISABLE = 2;
+
+    const HOMEDIR_REQUIRED = 8;
+
+    const LOCKOUT = 16;
+
+    const PASSWD_NOTREQD = 32;
+
+    const ENCRYPTED_TEXT_PWD_ALLOWED = 128;
+
+    const TEMP_DUPLICATE_ACCOUNT = 256;
+
+    const NORMAL_ACCOUNT = 512;
+
+    const INTERDOMAIN_TRUST_ACCOUNT = 2048;
+
+    const WORKSTATION_TRUST_ACCOUNT = 4096;
+
+    const SERVER_TRUST_ACCOUNT = 8192;
+
+    const DONT_EXPIRE_PASSWORD = 65536;
+
+    const MNS_LOGON_ACCOUNT = 131072;
+
+    const SMARTCARD_REQUIRED = 262144;
+
+    const TRUSTED_FOR_DELEGATION = 524288;
+
+    const NOT_DELEGATED = 1048576;
+
+    const USE_DES_KEY_ONLY = 2097152;
+
+    const DONT_REQ_PREAUTH = 4194304;
+
+    const PASSWORD_EXPIRED = 8388608;
+
+    const TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216;
+
+    const PARTIAL_SECRETS_ACCOUNT = 67108864;
+
+    /**
+     * Stores the values to be added together to
+     * build the user account control integer.
+     *
+     * @var array
+     */
+    protected $values = [];
+
+    /**
+     * Constructor.
+     *
+     * @param int $flag
+     */
+    public function __construct($flag = null)
+    {
+        if (!is_null($flag)) {
+            $this->apply($flag);
+        }
+    }
+
+    /**
+     * Returns the account control integer as a string
+     * when the object is casted as a string.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string) $this->getValue();
+    }
+
+    /**
+     * Returns the account control integer when
+     * the object is casted as an integer.
+     *
+     * @return int
+     */
+    public function __toInt()
+    {
+        return $this->getValue();
+    }
+
+    /**
+     * Applies the specified flag.
+     *
+     * @param int $flag
+     */
+    public function apply($flag)
+    {
+        $flags = [];
+
+        for ($i = 0; $i <= 26; $i++) {
+            if ((int) $flag & (1 << $i)) {
+                array_push($flags, 1 << $i);
+            }
+        }
+
+        $this->setValues($flags);
+    }
+
+    /**
+     * The logon script will be run.
+     *
+     * @return AccountControl
+     */
+    public function runLoginScript()
+    {
+        return $this->add(static::SCRIPT);
+    }
+
+    /**
+     * The user account is locked.
+     *
+     * @return AccountControl
+     */
+    public function accountIsLocked()
+    {
+        return $this->add(static::LOCKOUT);
+    }
+
+    /**
+     * The user account is disabled.
+     *
+     * @return AccountControl
+     */
+    public function accountIsDisabled()
+    {
+        return $this->add(static::ACCOUNTDISABLE);
+    }
+
+    /**
+     * This is an account for users whose primary account is in another domain.
+     *
+     * This account provides user access to this domain, but not to any domain that
+     * trusts this domain. This is sometimes referred to as a local user account.
+     *
+     * @return AccountControl
+     */
+    public function accountIsTemporary()
+    {
+        return $this->add(static::TEMP_DUPLICATE_ACCOUNT);
+    }
+
+    /**
+     * This is a default account type that represents a typical user.
+     *
+     * @return AccountControl
+     */
+    public function accountIsNormal()
+    {
+        return $this->add(static::NORMAL_ACCOUNT);
+    }
+
+    /**
+     * This is a permit to trust an account for a system domain that trusts other domains.
+     *
+     * @return AccountControl
+     */
+    public function accountIsForInterdomain()
+    {
+        return $this->add(static::INTERDOMAIN_TRUST_ACCOUNT);
+    }
+
+    /**
+     * This is a computer account for a computer that is running Microsoft
+     * Windows NT 4.0 Workstation, Microsoft Windows NT 4.0 Server, Microsoft
+     * Windows 2000 Professional, or Windows 2000 Server and is a member of this domain.
+     *
+     * @return AccountControl
+     */
+    public function accountIsForWorkstation()
+    {
+        return $this->add(static::WORKSTATION_TRUST_ACCOUNT);
+    }
+
+    /**
+     * This is a computer account for a domain controller that is a member of this domain.
+     *
+     * @return AccountControl
+     */
+    public function accountIsForServer()
+    {
+        return $this->add(static::SERVER_TRUST_ACCOUNT);
+    }
+
+    /**
+     * This is an MNS logon account.
+     *
+     * @return AccountControl
+     */
+    public function accountIsMnsLogon()
+    {
+        return $this->add(static::MNS_LOGON_ACCOUNT);
+    }
+
+    /**
+     * (Windows 2000/Windows Server 2003) This account does
+     * not require Kerberos pre-authentication for logging on.
+     *
+     * @return AccountControl
+     */
+    public function accountDoesNotRequirePreAuth()
+    {
+        return $this->add(static::DONT_REQ_PREAUTH);
+    }
+
+    /**
+     * When this flag is set, it forces the user to log on by using a smart card.
+     *
+     * @return AccountControl
+     */
+    public function accountRequiresSmartCard()
+    {
+        return $this->add(static::SMARTCARD_REQUIRED);
+    }
+
+    /**
+     * (Windows Server 2008/Windows Server 2008 R2) The account is a read-only domain controller (RODC).
+     *
+     * This is a security-sensitive setting. Removing this setting from an RODC compromises security on that server.
+     *
+     * @return AccountControl
+     */
+    public function accountIsReadOnly()
+    {
+        return $this->add(static::PARTIAL_SECRETS_ACCOUNT);
+    }
+
+    /**
+     * The home folder is required.
+     *
+     * @return AccountControl
+     */
+    public function homeFolderIsRequired()
+    {
+        return $this->add(static::HOMEDIR_REQUIRED);
+    }
+
+    /**
+     * No password is required.
+     *
+     * @return AccountControl
+     */
+    public function passwordIsNotRequired()
+    {
+        return $this->add(static::PASSWD_NOTREQD);
+    }
+
+    /**
+     * The user cannot change the password. This is a permission on the user's object.
+     *
+     * For information about how to programmatically set this permission, visit the following link:
+     * 
+     * @link http://msdn2.microsoft.com/en-us/library/aa746398.aspx
+     *
+     * @return AccountControl
+     */
+    public function passwordCannotBeChanged()
+    {
+        return $this->add(static::PASSWD_NOTREQD);
+    }
+
+    /**
+     * Represents the password, which should never expire on the account.
+     *
+     * @return AccountControl
+     */
+    public function passwordDoesNotExpire()
+    {
+        return $this->add(static::DONT_EXPIRE_PASSWORD);
+    }
+
+    /**
+     * (Windows 2000/Windows Server 2003) The user's password has expired.
+     *
+     * @return AccountControl
+     */
+    public function passwordIsExpired()
+    {
+        return $this->add(static::PASSWORD_EXPIRED);
+    }
+
+    /**
+     * The user can send an encrypted password.
+     *
+     * @return AccountControl
+     */
+    public function allowEncryptedTextPassword()
+    {
+        return $this->add(static::ENCRYPTED_TEXT_PWD_ALLOWED);
+    }
+
+    /**
+     * When this flag is set, the service account (the user or computer account)
+     * under which a service runs is trusted for Kerberos delegation.
+     *
+     * Any such service can impersonate a client requesting the service.
+     *
+     * To enable a service for Kerberos delegation, you must set this
+     * flag on the userAccountControl property of the service account.
+     *
+     * @return AccountControl
+     */
+    public function trustForDelegation()
+    {
+        return $this->add(static::TRUSTED_FOR_DELEGATION);
+    }
+
+    /**
+     * (Windows 2000/Windows Server 2003) The account is enabled for delegation.
+     *
+     * This is a security-sensitive setting. Accounts that have this option enabled
+     * should be tightly controlled. This setting lets a service that runs under the
+     * account assume a client's identity and authenticate as that user to other remote
+     * servers on the network.
+     *
+     * @return AccountControl
+     */
+    public function trustToAuthForDelegation()
+    {
+        return $this->add(static::TRUSTED_TO_AUTH_FOR_DELEGATION);
+    }
+
+    /**
+     * When this flag is set, the security context of the user is not delegated to a
+     * service even if the service account is set as trusted for Kerberos delegation.
+     *
+     * @return AccountControl
+     */
+    public function doNotTrustForDelegation()
+    {
+        return $this->add(static::NOT_DELEGATED);
+    }
+
+    /**
+     * (Windows 2000/Windows Server 2003) Restrict this principal to
+     * use only Data Encryption Standard (DES) encryption types for keys.
+     *
+     * @return AccountControl
+     */
+    public function useDesKeyOnly()
+    {
+        return $this->add(static::USE_DES_KEY_ONLY);
+    }
+
+    /**
+     * Returns the complete account control value.
+     *
+     * @return int
+     */
+    public function getValue()
+    {
+        $total = 0;
+
+        foreach ($this->values as $value) {
+            $total = $total + $value;
+        }
+
+        return $total;
+    }
+
+    /**
+     * Returns the account control's values.
+     *
+     * @return array
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+
+    /**
+     * Sets the account control values.
+     *
+     * @param array $flags
+     */
+    public function setValues(array $flags)
+    {
+        $this->values = $flags;
+    }
+
+    /**
+     * Applies the inserted value to the values property array.
+     *
+     * @param int $value
+     *
+     * @return AccountControl
+     */
+    protected function add($value)
+    {
+        // Use the value as a key so if the same value
+        // is used, it will always be overwritten
+        $this->values[$value] = $value;
+
+        return $this;
+    }
+}

+ 371 - 0
api/vendor/adldap2/adldap2/src/Models/Attributes/DistinguishedName.php

@@ -0,0 +1,371 @@
+<?php
+
+namespace Adldap\Models\Attributes;
+
+use Adldap\Utilities;
+use Adldap\Schemas\ActiveDirectory;
+use Adldap\Schemas\SchemaInterface;
+
+class DistinguishedName
+{
+    /**
+     * The common names in the DN.
+     *
+     * @var array
+     */
+    public $commonNames = [];
+
+    /**
+     * The uid's in the DN.
+     *
+     * @var array
+     */
+    public $userIds = [];
+
+    /**
+     * The organizational units in the DN.
+     *
+     * @var array
+     */
+    public $organizationUnits = [];
+
+    /**
+     * The domain components in the DN.
+     *
+     * @var array
+     */
+    public $domainComponents = [];
+
+    /**
+     * The organization names in the DN.
+     *
+     * @var array
+     */
+    public $organizationNames = [];
+
+    /**
+     * The current LDAP schema.
+     *
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * The RDN attribute types.
+     *
+     * @var array
+     */
+    protected $types = [
+        'o',
+        'dc',
+        'ou',
+        'uid',
+        'cn',
+    ];
+
+    /**
+     * Constructor.
+     *
+     * @param mixed           $baseDn
+     * @param SchemaInterface $schema
+     */
+    public function __construct($baseDn = null, SchemaInterface $schema = null)
+    {
+        $this->setBase($baseDn)
+            ->setSchema($schema);
+    }
+
+    /**
+     * Returns the complete distinguished name.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->get();
+    }
+
+    /**
+     * Returns the complete distinguished name.
+     *
+     * @return string
+     */
+    public function get()
+    {
+        return $this->assemble();
+    }
+
+    /**
+     * Adds a domain component.
+     *
+     * @param string $dc
+     *
+     * @return DistinguishedName
+     */
+    public function addDc($dc)
+    {
+        $this->domainComponents[] = $dc;
+
+        return $this;
+    }
+
+    /**
+     * Removes a domain component.
+     *
+     * @param string $dc
+     *
+     * @return DistinguishedName
+     */
+    public function removeDc($dc)
+    {
+        $this->domainComponents = array_diff($this->domainComponents, [$dc]);
+
+        return $this;
+    }
+
+    /**
+     * Adds an organization name.
+     *
+     * @param string $o
+     *
+     * @return $this
+     */
+    public function addO($o)
+    {
+        $this->organizationNames[] = $o;
+
+        return $this;
+    }
+
+    /**
+     * Removes an organization name.
+     *
+     * @param string $o
+     *
+     * @return DistinguishedName
+     */
+    public function removeO($o)
+    {
+        $this->organizationNames = array_diff($this->organizationNames, [$o]);
+
+        return $this;
+    }
+
+    /**
+     * Add a user identifier.
+     *
+     * @param string $uid
+     *
+     * @return DistinguishedName
+     */
+    public function addUid($uid)
+    {
+        $this->userIds[] = $uid;
+
+        return $this;
+    }
+
+    /**
+     * Removes a user identifier.
+     *
+     * @param string $uid
+     *
+     * @return DistinguishedName
+     */
+    public function removeUid($uid)
+    {
+        $this->userIds = array_diff($this->userIds, [$uid]);
+
+        return $this;
+    }
+
+    /**
+     * Adds a common name.
+     *
+     * @param string $cn
+     *
+     * @return DistinguishedName
+     */
+    public function addCn($cn)
+    {
+        $this->commonNames[] = $cn;
+
+        return $this;
+    }
+
+    /**
+     * Removes a common name.
+     *
+     * @param string $cn
+     *
+     * @return DistinguishedName
+     */
+    public function removeCn($cn)
+    {
+        $this->commonNames = array_diff($this->commonNames, [$cn]);
+
+        return $this;
+    }
+
+    /**
+     * Adds an organizational unit.
+     *
+     * @param string $ou
+     *
+     * @return DistinguishedName
+     */
+    public function addOu($ou)
+    {
+        $this->organizationUnits[] = $ou;
+
+        return $this;
+    }
+
+    /**
+     * Removes an organizational unit.
+     *
+     * @param string $ou
+     *
+     * @return DistinguishedName
+     */
+    public function removeOu($ou)
+    {
+        $this->organizationUnits = array_diff($this->organizationUnits, [$ou]);
+
+        return $this;
+    }
+
+    /**
+     * Sets the base RDN of the distinguished name.
+     *
+     * @param string|DistinguishedName $base
+     *
+     * @return DistinguishedName
+     */
+    public function setBase($base)
+    {
+        // Typecast base to string in case we've been given
+        // an instance of the distinguished name object.
+        $base = (string) $base;
+
+        // If the base DN isn't null we'll try to explode it.
+        $base = Utilities::explodeDn($base, false) ?: [];
+
+        // Remove the count key from the exploded distinguished name.
+        unset($base['count']);
+
+        foreach ($base as $key => $rdn) {
+            // We'll break the RDN into pieces
+            $pieces = explode('=', $rdn) ?: [];
+
+            // If there's exactly 2 pieces, then we can work with it.
+            if (count($pieces) === 2) {
+                $attribute = ucfirst($pieces[0]);
+
+                $method = 'add'.$attribute;
+
+                if (method_exists($this, $method)) {
+                    // We see what type of RDN it is and add each accordingly.
+                    call_user_func_array([$this, $method], [$pieces[1]]);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the schema for the distinguished name.
+     *
+     * @param SchemaInterface|null $schema
+     *
+     * @return DistinguishedName
+     */
+    public function setSchema(SchemaInterface $schema = null)
+    {
+        $this->schema = $schema ?: new ActiveDirectory();
+
+        return $this;
+    }
+
+    /**
+     * Assembles all of the RDNs and returns the result.
+     *
+     * @return string
+     */
+    public function assemble()
+    {
+        return implode(',', array_filter([
+            $this->assembleCns(),
+            $this->assembleUids(),
+            $this->assembleOus(),
+            $this->assembleDcs(),
+            $this->assembleOs(),
+        ]));
+    }
+
+    /**
+     * Assembles the common names in the distinguished name.
+     *
+     * @return string
+     */
+    public function assembleCns()
+    {
+        return $this->assembleRdns($this->schema->commonName(), $this->commonNames);
+    }
+
+    /**
+     * Assembles the user ID's in the distinguished name.
+     *
+     * @return string
+     */
+    public function assembleUids()
+    {
+        return $this->assembleRdns($this->schema->userId(), $this->userIds);
+    }
+
+    /**
+     * Assembles the organizational units in the distinguished Name.
+     *
+     * @return string
+     */
+    public function assembleOus()
+    {
+        return $this->assembleRdns($this->schema->organizationalUnitShort(), $this->organizationUnits);
+    }
+
+    /**
+     * Assembles the domain components in the distinguished Name.
+     *
+     * @return string
+     */
+    public function assembleDcs()
+    {
+        return $this->assembleRdns($this->schema->domainComponent(), $this->domainComponents);
+    }
+
+    /**
+     * Assembles the organization names in the distinguished name.
+     *
+     * @return string
+     */
+    public function assembleOs()
+    {
+        return $this->assembleRdns($this->schema->organizationName(), $this->organizationNames);
+    }
+
+    /**
+     * Assembles an RDN with the specified attribute and value.
+     *
+     * @param string $attribute
+     * @param array  $values
+     *
+     * @return string
+     */
+    protected function assembleRdns($attribute, array $values = [])
+    {
+        return implode(',', array_map(function ($value) use ($attribute) {
+            return sprintf('%s=%s', $attribute, Utilities::escape($value, '', 2));
+        }, $values));
+    }
+}

+ 154 - 0
api/vendor/adldap2/adldap2/src/Models/Attributes/Guid.php

@@ -0,0 +1,154 @@
+<?php
+
+namespace Adldap\Models\Attributes;
+
+use Adldap\Utilities;
+use InvalidArgumentException;
+
+class Guid
+{
+    /**
+     * The string GUID value.
+     *
+     * @var string
+     */
+    protected $value;
+
+    /**
+     * The guid structure in order by section to parse using substr().
+     *
+     * @author Chad Sikorra <Chad.Sikorra@gmail.com>
+     * @link https://github.com/ldaptools/ldaptools
+     *
+     * @var array
+     */
+    protected $guidSections = [
+        [[-26, 2], [-28, 2], [-30, 2], [-32, 2]],
+        [[-22, 2], [-24, 2]],
+        [[-18, 2], [-20, 2]],
+        [[-16, 4]],
+        [[-12, 12]],
+    ];
+
+    /**
+     * The hexadecimal octet order based on string position.
+     *
+     * @author Chad Sikorra <Chad.Sikorra@gmail.com>
+     * @link https://github.com/ldaptools/ldaptools
+     *
+     * @var array
+     */
+    protected $octetSections = [
+        [6, 4, 2 ,0],
+        [10, 8],
+        [14, 12],
+        [16, 18, 20, 22, 24, 26, 28, 30]
+    ];
+
+    /**
+     * Determines if the specified GUID is valid.
+     *
+     * @param string $guid
+     *
+     * @return bool
+     */
+    public static function isValid($guid)
+    {
+        return Utilities::isValidGuid($guid);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $value
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct($value)
+    {
+        if (static::isValid($value)) {
+            $this->value = $value;
+        } else if ($value = $this->binaryGuidToString($value)) {
+            $this->value = $value;
+        } else {
+            throw new InvalidArgumentException("Invalid Binary / String GUID.");
+        }
+    }
+
+    /**
+     * Returns the string value of the GUID.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getValue();
+    }
+
+    /**
+     * Returns the string value of the SID.
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Get the binary representation of the GUID string.
+     *
+     * @return string
+     */
+    public function getBinary()
+    {
+        $data = '';
+
+        $guid = str_replace('-', '', $this->value);
+
+        foreach ($this->octetSections as $section) {
+            $data .= $this->parseSection($guid, $section, true);
+        }
+
+        return hex2bin($data);
+    }
+
+    /**
+     * Returns the string variant of a binary GUID.
+     *
+     * @param string $binary
+     *
+     * @return string|null
+     */
+    protected function binaryGuidToString($binary)
+    {
+        return Utilities::binaryGuidToString($binary);
+    }
+
+    /**
+     * Return the specified section of the hexadecimal string.
+     *
+     * @author Chad Sikorra <Chad.Sikorra@gmail.com>
+     * @link 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.
+     *
+     * @return string The concatenated sections in upper-case.
+     */
+    protected function parseSection($hex, array $sections, $octet = false)
+    {
+        $parsedString = '';
+
+        foreach ($sections as $section) {
+            $start = $octet ? $section : $section[0];
+
+            $length = $octet ? 2 : $section[1];
+
+            $parsedString .= substr($hex, $start, $length);
+        }
+
+        return $parsedString;
+    }
+}

+ 101 - 0
api/vendor/adldap2/adldap2/src/Models/Attributes/Sid.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace Adldap\Models\Attributes;
+
+use Adldap\Utilities;
+use InvalidArgumentException;
+
+class Sid
+{
+    /**
+     * The string SID value.
+     *
+     * @var string
+     */
+    protected $value;
+
+    /**
+     * Determines if the specified SID is valid.
+     *
+     * @param string $sid
+     *
+     * @return bool
+     */
+    public static function isValid($sid)
+    {
+        return Utilities::isValidSid($sid);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $value
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct($value)
+    {
+        if (static::isValid($value)) {
+            $this->value = $value;
+        } else if ($value = $this->binarySidToString($value)) {
+            $this->value = $value;
+        } else {
+            throw new InvalidArgumentException("Invalid Binary / String SID.");
+        }
+    }
+
+    /**
+     * Returns the string value of the SID.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->getValue();
+    }
+
+    /**
+     * Returns the string value of the SID.
+     *
+     * @return string
+     */
+    public function getValue()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Returns the binary variant of the SID.
+     *
+     * @return string
+     */
+    public function getBinary()
+    {
+        $sid = explode('-', ltrim($this->value, 'S-'));
+
+        $level = (int) array_shift($sid);
+
+        $authority = (int) array_shift($sid);
+
+        $subAuthorities = array_map('intval', $sid);
+
+        $params = array_merge(
+            ['C2xxNV*', $level, count($subAuthorities), $authority],
+            $subAuthorities
+        );
+
+        return call_user_func_array('pack', $params);
+    }
+
+    /**
+     * Returns the string variant of a binary SID.
+     *
+     * @param string $binary
+     *
+     * @return string|null
+     */
+    protected function binarySidToString($binary)
+    {
+        return Utilities::binarySidToString($binary);
+    }
+}

+ 245 - 0
api/vendor/adldap2/adldap2/src/Models/BatchModification.php

@@ -0,0 +1,245 @@
+<?php
+
+namespace Adldap\Models;
+
+/**
+ * Class BatchModification
+ *
+ * A utility class to assist in the creation of LDAP
+ * batch modifications and ensure their validity.
+ *
+ * @package Adldap\Models
+ */
+class BatchModification
+{
+    /**
+     * The array keys to be used in batch modifications.
+     */
+    const KEY_ATTRIB = 'attrib';
+    const KEY_MODTYPE = 'modtype';
+    const KEY_VALUES = 'values';
+
+    /**
+     * The original value of the attribute before modification.
+     *
+     * @var null
+     */
+    protected $original = null;
+
+    /**
+     * The attribute of the modification.
+     *
+     * @var int|string
+     */
+    protected $attribute;
+
+    /**
+     * The values of the modification.
+     *
+     * @var array
+     */
+    protected $values = [];
+
+    /**
+     * The modtype integer of the batch modification.
+     *
+     * @var int
+     */
+    protected $type;
+
+    /**
+     * Constructor.
+     *
+     * @param string|null      $attribute
+     * @param string|int|null  $type
+     * @param array            $values
+     */
+    public function __construct($attribute = null, $type = null, $values = [])
+    {
+        $this->setAttribute($attribute)
+            ->setType($type)
+            ->setValues($values);
+    }
+
+    /**
+     * Sets the original value of the attribute before modification.
+     *
+     * @param mixed $original
+     *
+     * @return $this
+     */
+    public function setOriginal($original = null)
+    {
+        $this->original = $original;
+
+        return $this;
+    }
+
+    /**
+     * Returns the original value of the attribute before modification.
+     *
+     * @return mixed
+     */
+    public function getOriginal()
+    {
+        return $this->original;
+    }
+
+    /**
+     * Sets the attribute of the modification.
+     *
+     * @param string $attribute
+     *
+     * @return $this
+     */
+    public function setAttribute($attribute)
+    {
+        $this->attribute = $attribute;
+
+        return $this;
+    }
+
+    /**
+     * Returns the attribute of the modification.
+     *
+     * @return string
+     */
+    public function getAttribute()
+    {
+        return $this->attribute;
+    }
+
+    /**
+     * Sets the values of the modification.
+     *
+     * @param array $values
+     *
+     * @return $this
+     */
+    public function setValues(array $values = [])
+    {
+        $this->values = array_map(function($value) {
+            // We need to make sure all values given to a batch modification are
+            // strings, otherwise we'll receive an LDAP exception when
+            // we try to process the modification.
+            return (string) $value;
+        }, $values);
+
+        return $this;
+    }
+
+    /**
+     * Returns the values of the modification.
+     *
+     * @return array
+     */
+    public function getValues()
+    {
+        return $this->values;
+    }
+
+    /**
+     * Sets the type of the modification.
+     *
+     * @param int $type
+     *
+     * @return $this
+     */
+    public function setType($type)
+    {
+        $this->type = $type;
+
+        return $this;
+    }
+
+    /**
+     * Returns the type of the modification.
+     *
+     * @return int
+     */
+    public function getType()
+    {
+        return $this->type;
+    }
+
+    /**
+     * Determines if the batch modification
+     * is valid in its current state.
+     *
+     * @return bool
+     */
+    public function isValid()
+    {
+        return ! is_null($this->get());
+    }
+
+    /**
+     * Builds the type of modification automatically
+     * based on the current and original values.
+     *
+     * @return $this
+     */
+    public function build()
+    {
+        $filtered = array_diff(
+            array_map('trim', $this->values),
+            ['']
+        );
+
+        if (is_null($this->original)) {
+            // If the original value is null, we'll assume
+            // that the attribute doesn't exist yet.
+            if (!empty($filtered)) {
+                // If the filtered array is not empty, we'll
+                // assume the developer is looking to
+                // add attributes to the model.
+                $this->setType(LDAP_MODIFY_BATCH_ADD);
+            }
+
+            // If the filtered array is empty and there is no original
+            // value, then we can ignore this attribute since
+            // we can't push null values to AD.
+        } else {
+            if (empty($filtered)) {
+                // If there's an original value and the array is
+                // empty then we can assume the developer is
+                // looking to completely remove all values
+                // of the specified attribute.
+                $this->setType(LDAP_MODIFY_BATCH_REMOVE_ALL);
+            } else {
+                // If the array isn't empty then we can assume the
+                // developer is trying to replace all attributes.
+                $this->setType(LDAP_MODIFY_BATCH_REPLACE);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Returns the built batch modification array.
+     *
+     * @return array|null
+     */
+    public function get()
+    {
+        switch ($this->type) {
+            case LDAP_MODIFY_BATCH_REMOVE_ALL:
+                // A values key cannot be provided when
+                // a remove all type is selected.
+                return [static::KEY_ATTRIB => $this->attribute, static::KEY_MODTYPE => $this->type];
+            case LDAP_MODIFY_BATCH_REMOVE:
+                // Fallthrough.
+            case LDAP_MODIFY_BATCH_ADD:
+            case LDAP_MODIFY_BATCH_REPLACE:
+                return [
+                    static::KEY_ATTRIB => $this->attribute,
+                    static::KEY_MODTYPE => $this->type,
+                    static::KEY_VALUES => $this->values,
+                ];
+            default:
+                // If the modtype isn't recognized, we'll return null.
+                return;
+        }
+    }
+}

+ 93 - 0
api/vendor/adldap2/adldap2/src/Models/Computer.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\Models\Concerns\HasDescription;
+use Adldap\Models\Concerns\HasUserAccountControl;
+use Adldap\Models\Concerns\HasLastLogonAndLogOff;
+use Adldap\Models\Concerns\HasCriticalSystemObject;
+
+/**
+ * Class Computer
+ *
+ * Represents an LDAP computer / server.
+ *
+ * @package Adldap\Models
+ */
+class Computer extends Entry
+{
+    use HasCriticalSystemObject,
+        HasDescription,
+        HasLastLogonAndLogOff,
+        HasUserAccountControl;
+
+    /**
+     * Returns the computers operating system.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679076(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getOperatingSystem()
+    {
+        return $this->getFirstAttribute($this->schema->operatingSystem());
+    }
+
+    /**
+     * Returns the computers operating system version.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679079(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getOperatingSystemVersion()
+    {
+        return $this->getFirstAttribute($this->schema->operatingSystemVersion());
+    }
+
+    /**
+     * Returns the computers operating system service pack.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679078(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getOperatingSystemServicePack()
+    {
+        return $this->getFirstAttribute($this->schema->operatingSystemServicePack());
+    }
+
+    /**
+     * Returns the computers DNS host name.
+     *
+     * @return string
+     */
+    public function getDnsHostName()
+    {
+        return $this->getFirstAttribute($this->schema->dnsHostName());
+    }
+
+    /**
+     * Returns the computers bad password time.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675243(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getBadPasswordTime()
+    {
+        return $this->getFirstAttribute($this->schema->badPasswordTime());
+    }
+
+    /**
+     * Returns the computers account expiry date.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getAccountExpiry()
+    {
+        return $this->getFirstAttribute($this->schema->accountExpires());
+    }
+}

+ 319 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasAttributes.php

@@ -0,0 +1,319 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+use Illuminate\Support\Arr;
+
+trait HasAttributes
+{
+    /**
+     * The default output date format for all time related methods.
+     *
+     * Default format is suited for MySQL timestamps.
+     *
+     * @var string
+     */
+    public $dateFormat = 'Y-m-d H:i:s';
+
+    /**
+     * The format that is used to convert AD timestamps to unix timestamps.
+     *
+     * @var string
+     */
+    protected $timestampFormat = 'YmdHis.0Z';
+
+    /**
+     * The models attributes.
+     *
+     * @var array
+     */
+    protected $attributes = [];
+    
+    /**
+     * The models original attributes.
+     *
+     * @var array
+     */
+    protected $original = [];
+
+    /**
+     * Dynamically retrieve attributes on the object.
+     *
+     * @param mixed $key
+     *
+     * @return bool
+     */
+    public function __get($key)
+    {
+        return $this->getAttribute($key);
+    }
+
+    /**
+     * Dynamically set attributes on the object.
+     *
+     * @param mixed $key
+     * @param mixed $value
+     *
+     * @return $this
+     */
+    public function __set($key, $value)
+    {
+        return $this->setAttribute($key, $value);
+    }
+
+    /**
+     * Synchronizes the models original attributes
+     * with the model's current attributes.
+     *
+     * @return $this
+     */
+    public function syncOriginal()
+    {
+        $this->original = $this->attributes;
+
+        return $this;
+    }
+
+    /**
+     * Returns the models attribute with the specified key.
+     *
+     * If a sub-key is specified, it will try and
+     * retrieve it from the parent keys array.
+     *
+     * @param int|string $key
+     * @param int|string $subKey
+     *
+     * @return mixed
+     */
+    public function getAttribute($key, $subKey = null)
+    {
+        if (is_null($subKey) && $this->hasAttribute($key)) {
+            return $this->attributes[$key];
+        } elseif ($this->hasAttribute($key, $subKey)) {
+            return $this->attributes[$key][$subKey];
+        }
+    }
+
+    /**
+     * Returns the first attribute by the specified key.
+     *
+     * @param string $key
+     *
+     * @return mixed
+     */
+    public function getFirstAttribute($key)
+    {
+        return $this->getAttribute($key, 0);
+    }
+
+    /**
+     * Returns all of the models attributes.
+     *
+     * @return array
+     */
+    public function getAttributes()
+    {
+        return $this->attributes;
+    }
+
+    /**
+     * Fills the entry with the supplied attributes.
+     *
+     * @param array $attributes
+     *
+     * @return $this
+     */
+    public function fill(array $attributes = [])
+    {
+        foreach ($attributes as $key => $value) {
+            $this->setAttribute($key, $value);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets an attributes value by the specified key and sub-key.
+     *
+     * @param int|string $key
+     * @param mixed      $value
+     * @param int|string $subKey
+     *
+     * @return $this
+     */
+    public function setAttribute($key, $value, $subKey = null)
+    {
+        // Normalize key.
+        $key = $this->normalizeAttributeKey($key);
+
+        // If the key is equal to 'dn', we'll automatically
+        // change it to the full attribute name.
+        $key = ($key == 'dn' ? $this->schema->distinguishedName() : $key);
+
+        if (is_null($subKey)) {
+            // We need to ensure all attributes are set as arrays so all
+            // of our model methods retrieve attributes correctly.
+            $this->attributes[$key] = is_array($value) ? $value : [$value];
+        } else {
+            $this->attributes[$key][$subKey] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Sets the first attributes value by the specified key.
+     *
+     * @param int|string $key
+     * @param mixed      $value
+     *
+     * @return $this
+     */
+    public function setFirstAttribute($key, $value)
+    {
+        return $this->setAttribute($key, $value, 0);
+    }
+
+    /**
+     * Sets the attributes property.
+     *
+     * @param array $attributes
+     *
+     * @return $this
+     */
+    public function setRawAttributes(array $attributes = [])
+    {
+        $this->attributes = $this->filterRawAttributes($attributes);
+
+        if (Arr::has($attributes, 'dn')) {
+            $this->setDn($attributes['dn']);
+        }
+
+        $this->syncOriginal();
+
+        // Set exists to true since raw attributes are only
+        // set in the case of attributes being loaded by
+        // query results.
+        $this->exists = true;
+
+        return $this;
+    }
+
+    /**
+     * Filters the count key recursively from raw LDAP attributes.
+     *
+     * @param array        $attributes
+     * @param array|string $keys
+     *
+     * @return array
+     */
+    public function filterRawAttributes(array $attributes = [], $keys = ['count'])
+    {
+        $attributes = Arr::except($attributes, $keys);
+
+        array_walk($attributes, function (&$value) use ($keys) {
+            $value = is_array($value) ?
+                $this->filterRawAttributes($value, $keys) :
+                $value;
+        });
+
+        return $attributes;
+    }
+
+    /**
+     * Returns true / false if the specified attribute
+     * exists in the attributes array.
+     *
+     * @param int|string $key
+     * @param int|string $subKey
+     *
+     * @return bool
+     */
+    public function hasAttribute($key, $subKey = null)
+    {
+        if (is_null($subKey)) {
+            return Arr::has($this->attributes, $key);
+        }
+
+        return Arr::has($this->attributes, "$key.$subKey");
+    }
+
+    /**
+     * Returns the number of attributes inside
+     * the attributes property.
+     *
+     * @return int
+     */
+    public function countAttributes()
+    {
+        return count($this->getAttributes());
+    }
+
+    /**
+     * Returns the models original attributes.
+     *
+     * @return array
+     */
+    public function getOriginal()
+    {
+        return $this->original;
+    }
+
+    /**
+     * Get the attributes that have been changed since last sync.
+     *
+     * @return array
+     */
+    public function getDirty()
+    {
+        $dirty = [];
+
+        foreach ($this->attributes as $key => $value) {
+            if (! $this->originalIsEquivalent($key)) {
+                // We need to set reset the array's indices using array_values due to
+                // LDAP requiring consecutive indices (0, 1, 2 etc.)
+                $dirty[$key] = array_values($value);
+            }
+        }
+
+        return $dirty;
+    }
+
+    /**
+     * Returns a normalized attribute key.
+     *
+     * @param string $key
+     *
+     * @return string
+     */
+    protected function normalizeAttributeKey($key)
+    {
+        return strtolower($key);
+    }
+
+    /**
+     * Determine if the new and old values for a given key are equivalent.
+     *
+     * @param string $key
+     *
+     * @return bool
+     */
+    protected function originalIsEquivalent($key)
+    {
+        if (! array_key_exists($key, $this->original)) {
+            return false;
+        }
+
+        $current = $this->attributes[$key];
+
+        $original = $this->original[$key];
+
+        if ($current === $original) {
+            return true;
+        }
+
+        return  is_numeric($current) &&
+                is_numeric($original) &&
+                strcmp((string) $current, (string) $original) === 0;
+    }
+}

+ 18 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasCriticalSystemObject.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+trait HasCriticalSystemObject
+{
+    /**
+     * Returns true / false if the entry is a critical system object.
+     *
+     * @return null|bool
+     */
+    public function isCriticalSystemObject()
+    {
+        $attribute = $this->getFirstAttribute($this->schema->isCriticalSystemObject());
+
+        return $this->convertStringToBool($attribute);
+    }
+}

+ 30 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasDescription.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+trait HasDescription
+{
+    /**
+     * Returns the models's description.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675492(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getDescription()
+    {
+        return $this->getFirstAttribute($this->schema->description());
+    }
+
+    /**
+     * Sets the models's description.
+     *
+     * @param string $description
+     *
+     * @return $this
+     */
+    public function setDescription($description)
+    {
+        return $this->setFirstAttribute($this->schema->description(), $description);
+    }
+}

+ 42 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasLastLogonAndLogOff.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+trait HasLastLogonAndLogOff
+{
+    /**
+     * Returns the models's last log off date.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676822(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getLastLogOff()
+    {
+        return $this->getFirstAttribute($this->schema->lastLogOff());
+    }
+
+    /**
+     * Returns the models's last log on date.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676823(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getLastLogon()
+    {
+        return $this->getFirstAttribute($this->schema->lastLogOn());
+    }
+
+    /**
+     * Returns the models's last log on timestamp.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676824(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getLastLogonTimestamp()
+    {
+        return $this->getFirstAttribute($this->schema->lastLogOnTimestamp());
+    }
+}

+ 240 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasMemberOf.php

@@ -0,0 +1,240 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+use Adldap\Utilities;
+use Adldap\Models\User;
+use Adldap\Models\Group;
+use Illuminate\Support\Collection;
+
+trait HasMemberOf
+{
+    /**
+     * Adds the current model to the specified group.
+     *
+     * @param string|Group $group
+     *
+     * @return bool
+     */
+    public function addGroup($group)
+    {
+        if (is_string($group)) {
+            // If the group is a string, we'll assume the dev is passing
+            // in a DN string of the group. We'll try to locate it.
+            $group = $this->query->newInstance()->findByDn($group);
+        }
+
+        if ($group instanceof Group) {
+            // If the group is Group model instance, we can
+            // add the current models DN to the group.
+            return $group->addMember($this->getDn());
+        }
+
+        return false;
+    }
+
+    /**
+     * Removes the current model from the specified group.
+     *
+     * @param string|Group $group
+     *
+     * @return bool
+     */
+    public function removeGroup($group)
+    {
+        if (is_string($group)) {
+            // If the group is a string, we'll assume the dev is passing
+            // in a DN string of the group. We'll try to locate it.
+            $group = $this->query->newInstance()->findByDn($group);
+        }
+
+        if ($group instanceof Group) {
+            // If the group is Group model instance, we can
+            // remove the current models DN from the group.
+            return $group->removeMember($this->getDn());
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the models groups that it is apart of.
+     *
+     * If a recursive option is given, groups of groups
+     * are retrieved and then merged with
+     * the resulting collection.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677099(v=vs.85).aspx
+     *
+     * @param array $fields
+     * @param bool  $recursive
+     * @param array $visited
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function getGroups(array $fields = ['*'], $recursive = false, array $visited = [])
+    {
+        if (!in_array($this->schema->memberOf(), $fields)) {
+            // We want to make sure that we always select the memberof
+            // field in case developers want recursive members.
+            $fields = array_merge($fields, [$this->schema->memberOf()]);
+        }
+
+        $groups = $this->getGroupsByNames($this->memberOfDns(), $fields);
+
+        // We need to check if we're working with a User model. Only users
+        // contain a primary group. If we are, we'll merge the users
+        // primary group into the resulting collection.
+        if ($this instanceof User && $primary = $this->getPrimaryGroup()) {
+            $groups->push($primary);
+        }
+
+        // If recursive results are requested, we'll ask each group
+        // for their groups, and merge the resulting collection.
+        if ($recursive) {
+            /** @var Group $group */
+            foreach ($groups as $group) {
+                // We need to validate that we haven't already queried
+                // for this group's members so we don't allow
+                // infinite recursion in case of circular
+                // group dependencies in LDAP.
+                if (!in_array($group->getDn(), $visited)) {
+                    $visited[] = $group->getDn();
+
+                    $members = $group->getGroups($fields, $recursive, $visited);
+
+                    /** @var Group $member */
+                    foreach ($members as $member) {
+                        $visited[] = $member->getDn();
+                    }
+
+                    $groups = $groups->merge($members);
+                }
+            }
+        }
+
+        return $groups;
+    }
+
+    /**
+     * Returns the models groups names in a single dimension array.
+     *
+     * If a recursive option is given, groups of groups
+     * are retrieved and then merged with
+     * the resulting collection.
+     *
+     * @param bool $recursive
+     *
+     * @return array
+     */
+    public function getGroupNames($recursive = false)
+    {
+        $fields = [$this->schema->commonName(), $this->schema->memberOf()];
+
+        $names = $this->getGroups($fields, $recursive)->map(function (Group $group) {
+            return $group->getCommonName();
+        })->toArray();
+
+        return array_unique($names);
+    }
+
+    /**
+     * Determine if the current model is a member of the specified group(s).
+     *
+     * @param mixed $group
+     * @param bool  $recursive
+     *
+     * @return bool
+     */
+    public function inGroup($group, $recursive = false)
+    {
+        $memberOf = $this->getGroups(['cn'], $recursive);
+
+        if ($group instanceof Collection) {
+            // If we've been given a collection then we'll convert
+            // it to an array to normalize the value.
+            $group = $group->toArray();
+        }
+
+        $groups = is_array($group) ? $group : [$group];
+
+        foreach ($groups as $group) {
+            // We need to iterate through each given group that the
+            // model must be apart of, then go through the models
+            // actual groups and perform validation.
+            $exists = $memberOf->filter(function (Group $parent) use ($group) {
+                return $this->groupIsParent($group, $parent);
+            })->count() !== 0;
+
+            if (!$exists) {
+                // If the current group isn't at all contained
+                // in the memberOf collection, we'll
+                // return false here.
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Retrieves groups by their distinguished name.
+     *
+     * @param array $dns
+     * @param array $fields
+     *
+     * @return Collection
+     */
+    protected function getGroupsByNames(array $dns = [], $fields = [])
+    {
+        $query = $this->query->newInstance();
+
+        return $query->newCollection($dns)->map(function ($dn) use ($query, $fields) {
+            return $query->select($fields)->findByDn($dn);
+        })->filter(function ($group) {
+            return $group instanceof Group;
+        });
+    }
+
+    /**
+     * Returns the member distinguished names.
+     *
+     * @return array
+     */
+    protected function memberOfDns()
+    {
+        $dns = $this->getAttribute($this->schema->memberOf());
+
+        // Normalize returned distinguished names.
+        return is_array($dns) ? $dns : [];
+    }
+
+    /**
+     * Validates if the specified group is the given parent instance.
+     *
+     * @param Group|string $group
+     * @param Group        $parent
+     *
+     * @return bool
+     */
+    protected function groupIsParent($group, Group $parent)
+    {
+        if ($group instanceof Group) {
+            // We've been given a group instance, we'll compare their DNs.
+            return $parent->getDn() === $group->getDn();
+        }
+
+        if (Utilities::explodeDn($group)) {
+            // We've been given a DN, we'll compare it to the parents.
+            return $parent->getDn() === $group;
+        }
+
+        if (!empty($group)) {
+            // We've been given just a string, we'll
+            // compare it to the parents name.
+            return $parent->getCommonName() === $group;
+        }
+
+        return false;
+    }
+}

+ 50 - 0
api/vendor/adldap2/adldap2/src/Models/Concerns/HasUserAccountControl.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Adldap\Models\Concerns;
+
+use Adldap\Models\Attributes\AccountControl;
+
+trait HasUserAccountControl
+{
+    /**
+     * Returns the users user account control integer.
+     *
+     * @return string
+     */
+    public function getUserAccountControl()
+    {
+        return $this->getFirstAttribute($this->schema->userAccountControl());
+    }
+
+    /**
+     * Sets the users account control property.
+     *
+     * @param int|string|AccountControl $accountControl
+     *
+     * @return $this
+     */
+    public function setUserAccountControl($accountControl)
+    {
+        return $this->setAttribute($this->schema->userAccountControl(), (string) $accountControl);
+    }
+
+    /**
+     * Returns if the user is disabled.
+     *
+     * @return bool
+     */
+    public function isDisabled()
+    {
+        return ($this->getUserAccountControl() & AccountControl::ACCOUNTDISABLE) === AccountControl::ACCOUNTDISABLE;
+    }
+
+    /**
+     * Returns if the user is enabled.
+     *
+     * @return bool
+     */
+    public function isEnabled()
+    {
+        return $this->getUserAccountControl() === null ? false : !$this->isDisabled();
+    }
+}

+ 15 - 0
api/vendor/adldap2/adldap2/src/Models/Contact.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Adldap\Models;
+
+/**
+ * Class Contact
+ *
+ * Represents an LDAP contact.
+ *
+ * @package Adldap\Models
+ */
+class Contact extends Entry
+{
+    //
+}

+ 30 - 0
api/vendor/adldap2/adldap2/src/Models/Container.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\Models\Concerns\HasDescription;
+use Adldap\Models\Concerns\HasCriticalSystemObject;
+
+/**
+ * Class Container
+ *
+ * Represents an LDAP container.
+ *
+ * @package Adldap\Models
+ */
+class Container extends Entry
+{
+    use HasDescription, HasCriticalSystemObject;
+
+    /**
+     * Returns the containers system flags integer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680022(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getSystemFlags()
+    {
+        return $this->getFirstAttribute($this->schema->systemFlags());
+    }
+}

+ 15 - 0
api/vendor/adldap2/adldap2/src/Models/Entry.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Adldap\Models;
+
+/**
+ * Class Entry
+ *
+ * Represents an LDAP record that could not be identified as another type of model.
+ *
+ * @package Adldap\Models
+ */
+class Entry extends Model
+{
+    //
+}

+ 192 - 0
api/vendor/adldap2/adldap2/src/Models/Factory.php

@@ -0,0 +1,192 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\Query\Builder;
+use Adldap\Schemas\ActiveDirectory;
+use Adldap\Schemas\SchemaInterface;
+
+/**
+ * Class Factory
+ *
+ * Constructs and scopes LDAP queries.
+ *
+ * @package Adldap\Models
+ */
+class Factory
+{
+    /**
+     * @var Builder
+     */
+    protected $query;
+
+    /**
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * Constructor.
+     *
+     * @param Builder $builder
+     */
+    public function __construct(Builder $builder)
+    {
+        $this->setQuery($builder)
+            ->setSchema($builder->getSchema());
+    }
+
+    /**
+     * Sets the current query builder.
+     *
+     * @param Builder $builder
+     *
+     * @return $this
+     */
+    public function setQuery(Builder $builder)
+    {
+        $this->query = $builder;
+
+        return $this;
+    }
+
+    /**
+     * Sets the current schema.
+     *
+     * @param SchemaInterface|null $schema
+     *
+     * @return $this
+     */
+    public function setSchema(SchemaInterface $schema = null)
+    {
+        $this->schema = $schema ?: new ActiveDirectory();
+
+        return $this;
+    }
+
+    /**
+     * Creates a new generic LDAP entry instance.
+     *
+     * @param array $attributes
+     *
+     * @return Entry
+     */
+    public function entry(array $attributes = [])
+    {
+        $model = $this->schema->entryModel();
+
+        return (new $model($attributes, $this->query));
+    }
+
+    /**
+     * Creates a new user instance.
+     *
+     * @param array $attributes
+     *
+     * @return User
+     */
+    public function user(array $attributes = [])
+    {
+        $model = $this->schema->userModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), [
+                $this->schema->top(),
+                $this->schema->person(),
+                $this->schema->organizationalPerson(),
+                $this->schema->user(),
+            ]);
+    }
+
+    /**
+     * Creates a new organizational unit instance.
+     *
+     * @param array $attributes
+     *
+     * @return OrganizationalUnit
+     */
+    public function ou(array $attributes = [])
+    {
+        $model = $this->schema->organizationalUnitModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), [
+                $this->schema->top(),
+                $this->schema->organizationalUnit(),
+            ]);
+    }
+
+    /**
+     * Creates a new group instance.
+     *
+     * @param array $attributes
+     *
+     * @return Group
+     */
+    public function group(array $attributes = [])
+    {
+        $model = $this->schema->groupModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), [
+                $this->schema->top(),
+                $this->schema->objectCategoryGroup(),
+            ]);
+    }
+
+    /**
+     * Creates a new organizational unit instance.
+     *
+     * @param array $attributes
+     *
+     * @return Container
+     */
+    public function container(array $attributes = [])
+    {
+        $model = $this->schema->containerModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), $this->schema->organizationalUnit());
+    }
+
+    /**
+     * Creates a new user instance as a contact.
+     *
+     * @param array $attributes
+     *
+     * @return User
+     */
+    public function contact(array $attributes = [])
+    {
+        $model = $this->schema->contactModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), [
+                $this->schema->top(),
+                $this->schema->person(),
+                $this->schema->organizationalPerson(),
+                $this->schema->contact(),
+            ]);
+    }
+
+    /**
+     * Creates a new computer instance.
+     *
+     * @param array $attributes
+     *
+     * @return Computer
+     */
+    public function computer(array $attributes = [])
+    {
+        $model = $this->schema->computerModel();
+
+        return (new $model($attributes, $this->query))
+            ->setAttribute($this->schema->objectClass(), [
+                $this->schema->top(),
+                $this->schema->person(),
+                $this->schema->organizationalPerson(),
+                $this->schema->user(),
+                $this->schema->computer(),
+            ]);
+    }
+}

+ 269 - 0
api/vendor/adldap2/adldap2/src/Models/Group.php

@@ -0,0 +1,269 @@
+<?php
+
+namespace Adldap\Models;
+
+use InvalidArgumentException;
+use Adldap\Utilities;
+use Adldap\Models\Concerns\HasMemberOf;
+use Adldap\Models\Concerns\HasDescription;
+
+/**
+ * Class Group
+ *
+ * Represents an LDAP group (security / distribution).
+ *
+ * @package Adldap\Models
+ */
+class Group extends Entry
+{
+    use HasDescription, HasMemberOf;
+
+    /**
+     * Returns all users apart of the current group.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677097(v=vs.85).aspx
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function getMembers()
+    {
+        $members = $this->getMembersFromAttribute($this->schema->member());
+
+        if(count($members) === 0) {
+            $members = $this->getPaginatedMembers();
+        }
+
+        return $this->newCollection($members);
+    }
+
+    /**
+     * Returns the group's member names only.
+     *
+     * @return array
+     */
+    public function getMemberNames()
+    {
+        $members = [];
+
+        $dns = $this->getAttribute($this->schema->member()) ?: [];
+
+        foreach ($dns as $dn) {
+            $exploded = Utilities::explodeDn($dn);
+
+            if (array_key_exists(0, $exploded)) {
+                $members[] = $exploded[0];
+            }
+        }
+
+        return $members;
+    }
+
+    /**
+     * Sets the groups members using an array of user DNs.
+     *
+     * @param array $entries
+     *
+     * @return $this
+     */
+    public function setMembers(array $entries)
+    {
+        $this->setAttribute($this->schema->member(), $entries);
+
+        return $this;
+    }
+
+    /**
+     * Adds multiple entries to the current group.
+     *
+     * @param array $members
+     *
+     * @return bool
+     */
+    public function addMembers(array $members)
+    {
+        $members = array_map(function ($member) {
+            return $member instanceof Model
+                ? $member->getDn()
+                : $member;
+        }, $members);
+
+        $mod = $this->newBatchModification(
+            $this->schema->member(),
+            LDAP_MODIFY_BATCH_ADD,
+            $members
+        );
+
+        return $this->addModification($mod)->save();
+    }
+
+    /**
+     * Adds an entry to the current group.
+     *
+     * @param string|Entry $entry
+     *
+     * @throws InvalidArgumentException When the given entry is empty or contains no distinguished name.
+     *
+     * @return bool
+     */
+    public function addMember($entry)
+    {
+        $entry = ($entry instanceof Model ? $entry->getDn() : $entry);
+
+        if (is_null($entry)) {
+            throw new InvalidArgumentException(
+                'Cannot add member to group. The members distinguished name cannot be null.'
+            );
+        }
+
+        $mod = $this->newBatchModification(
+            $this->schema->member(),
+            LDAP_MODIFY_BATCH_ADD,
+            [$entry]
+        );
+
+        return $this->addModification($mod)->save();
+    }
+
+    /**
+     * Removes an entry from the current group.
+     *
+     * @param string|Entry $entry
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return bool
+     */
+    public function removeMember($entry)
+    {
+        $entry = ($entry instanceof Model ? $entry->getDn() : $entry);
+
+        if (is_null($entry)) {
+            throw new InvalidArgumentException(
+                'Cannot add member to group. The members distinguished name cannot be null.'
+            );
+        }
+
+        $mod = $this->newBatchModification(
+            $this->schema->member(),
+            LDAP_MODIFY_BATCH_REMOVE,
+            [$entry]
+        );
+
+        return $this->addModification($mod)->save();
+    }
+
+    /**
+     * Removes all members from the current group.
+     *
+     * @return bool
+     */
+    public function removeMembers()
+    {
+        $mod = $this->newBatchModification(
+            $this->schema->member(),
+            LDAP_MODIFY_BATCH_REMOVE_ALL
+        );
+
+        return $this->addModification($mod)->save();
+    }
+
+    /**
+     * Returns the group type integer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675935(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getGroupType()
+    {
+        return $this->getFirstAttribute($this->schema->groupType());
+    }
+
+    /**
+     * Retrieves group members by the specified model
+     * attribute using their distinguished name.
+     *
+     * @param $attribute
+     *
+     * @return array
+     */
+    protected function getMembersFromAttribute($attribute)
+    {
+        $members = [];
+
+        $dns = $this->getAttribute($attribute) ?: [];
+
+        foreach ($dns as $dn) {
+            $member = $this->query->newInstance()->findByDn($dn);
+
+            if ($member instanceof Model) {
+                $members[] = $member;
+            }
+        }
+
+        return $members;
+    }
+
+    /**
+     * Retrieves members that are contained in a member range.
+     *
+     * @return array
+     */
+    protected function getPaginatedMembers()
+    {
+        $members = [];
+
+        $keys = array_keys($this->attributes);
+
+        // We need to filter out the model attributes so
+        // we only retrieve the member range.
+        $attributes = array_values(array_filter($keys, function ($key) {
+            return strpos($key,'member;range') !== false;
+        }));
+
+        // We'll grab the member range key so we can run a
+        // regex on it to determine the range.
+        $key = reset($attributes);
+
+        preg_match_all(
+            '/member;range\=([0-9]{1,4})-([0-9*]{1,4})/',
+            $key,
+            $matches
+        );
+
+        if ($key && count($matches) == 3) {
+            $to = $matches[2][0];
+
+            $members = $this->getMembersFromAttribute($key);
+
+            // If the query already included all member results (indicated
+            // by the '*'), then we can return here. Otherwise we need
+            // to continue on and retrieve the rest.
+            if($to === '*') {
+                return $members;
+            }
+
+            $from = $to + 1;
+
+            // We'll determine the member range simply
+            // by doubling the selected from value.
+            $to = $from * 2;
+
+            // We'll need to query for the current model again but with
+            // a new range to retrieve the other members.
+            $group = $this->query->newInstance()->findByDn(
+                $this->getDn(),
+                [$this->query->getSchema()->memberRange($from, $to)]
+            );
+
+            // Finally, we'll merge our current members
+            // with the newly returned members.
+            $members = array_merge(
+                $members,
+                $group->getMembers()->toArray()
+            );
+        }
+
+        return $members;
+    }
+}

+ 1066 - 0
api/vendor/adldap2/adldap2/src/Models/Model.php

@@ -0,0 +1,1066 @@
+<?php
+
+namespace Adldap\Models;
+
+use DateTime;
+use ArrayAccess;
+use JsonSerializable;
+use InvalidArgumentException;
+use Illuminate\Support\Collection;
+use Adldap\Utilities;
+use Adldap\Query\Builder;
+use Adldap\Models\Attributes\Sid;
+use Adldap\Models\Attributes\Guid;
+use Adldap\Models\Attributes\DistinguishedName;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Models\Concerns\HasAttributes;
+use Adldap\Connections\ConnectionException;
+
+/**
+ * Class Model
+ *
+ * Represents an LDAP record and provides the ability to
+ * modify / retrieve data from the record.
+ *
+ * @package Adldap\Models
+ */
+abstract class Model implements ArrayAccess, JsonSerializable
+{
+    use HasAttributes;
+
+    /**
+     * Indicates if the model exists.
+     *
+     * @var bool
+     */
+    public $exists = false;
+
+    /**
+     * The models distinguished name.
+     *
+     * @var string
+     */
+    protected $dn;
+
+    /**
+     * The current query builder instance.
+     *
+     * @var Builder
+     */
+    protected $query;
+
+    /**
+     * The current LDAP attribute schema.
+     *
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * Contains the models modifications.
+     *
+     * @var array
+     */
+    protected $modifications = [];
+
+    /**
+     * Constructor.
+     *
+     * @param array   $attributes
+     * @param Builder $builder
+     */
+    public function __construct(array $attributes = [], Builder $builder)
+    {
+        $this->setQuery($builder)
+            ->setSchema($builder->getSchema())
+            ->fill($attributes);
+    }
+
+    /**
+     * Sets the current query builder.
+     *
+     * @param Builder $builder
+     *
+     * @return $this
+     */
+    public function setQuery(Builder $builder)
+    {
+        $this->query = $builder;
+
+        return $this;
+    }
+
+    /**
+     * Returns the current query builder.
+     *
+     * @return Builder
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Returns a new query builder instance.
+     *
+     * @return Builder
+     */
+    public function newQuery()
+    {
+        return $this->query->newInstance();
+    }
+
+    /**
+     * Returns a new batch modification.
+     *
+     * @param string|null     $attribute
+     * @param string|int|null $type
+     * @param array           $values
+     *
+     * @return BatchModification
+     */
+    public function newBatchModification($attribute = null, $type = null, $values = [])
+    {
+        return new BatchModification($attribute, $type, $values);
+    }
+
+    /**
+     * Returns a new collection with the specified items.
+     *
+     * @param mixed $items
+     *
+     * @return \Illuminate\Support\Collection
+     */
+    public function newCollection($items = [])
+    {
+        return new Collection($items);
+    }
+
+    /**
+     * Sets the current model schema.
+     *
+     * @param SchemaInterface $schema
+     *
+     * @return $this
+     */
+    public function setSchema(SchemaInterface $schema)
+    {
+        $this->schema = $schema;
+
+        return $this;
+    }
+
+    /**
+     * Returns the current model schema.
+     *
+     * @return SchemaInterface
+     */
+    public function getSchema()
+    {
+        return $this->schema;
+    }
+
+    /**
+     * Determine if the given offset exists.
+     *
+     * @param string $offset
+     *
+     * @return bool
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->{$offset});
+    }
+
+    /**
+     * Get the value for a given offset.
+     *
+     * @param string $offset
+     *
+     * @return mixed
+     */
+    public function offsetGet($offset)
+    {
+        return $this->{$offset};
+    }
+
+    /**
+     * Set the value at the given offset.
+     *
+     * @param string $offset
+     * @param mixed  $value
+     *
+     * @return void
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->{$offset} = $value;
+    }
+
+    /**
+     * Unset the value at the given offset.
+     *
+     * @param string $offset
+     *
+     * @return void
+     */
+    public function offsetUnset($offset)
+    {
+        unset($this->{$offset});
+    }
+
+    /**
+     * Convert the object into something JSON serializable.
+     *
+     * @return array
+     */
+    public function jsonSerialize()
+    {
+        $attributes = $this->getAttributes();
+
+        array_walk_recursive($attributes, function(&$val){
+            $val = utf8_encode($val);
+        });
+
+        // We'll replace the binary GUID and SID with
+        // their string equivalents for convenience.
+        return array_replace($attributes, [
+            $this->schema->objectGuid() => $this->getConvertedGuid(),
+            $this->schema->objectSid() => $this->getConvertedSid(),
+        ]);
+    }
+
+    /**
+     * Reload a fresh model instance from the directory.
+     *
+     * @return static|null
+     */
+    public function fresh()
+    {
+        $model = $this->query->newInstance()->findByDn($this->getDn());
+
+        return $model instanceof Model ? $model : null;
+    }
+
+    /**
+     * Synchronizes the current models attributes with the directory values.
+     *
+     * @return bool
+     */
+    public function syncRaw()
+    {
+        if ($model = $this->fresh()) {
+            $this->setRawAttributes($model->getAttributes());
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the models batch modifications to be processed.
+     *
+     * @return array
+     */
+    public function getModifications()
+    {
+        $this->buildModificationsFromDirty();
+
+        return $this->modifications;
+    }
+
+    /**
+     * Sets the models modifications array.
+     *
+     * @param array $modifications
+     *
+     * @return $this
+     */
+    public function setModifications(array $modifications = [])
+    {
+        $this->modifications = $modifications;
+
+        return $this;
+    }
+
+    /**
+     * Adds a batch modification to the models modifications array.
+     *
+     * @param array|BatchModification $mod
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return $this
+     */
+    public function addModification($mod = [])
+    {
+        if ($mod instanceof BatchModification) {
+            $mod = $mod->get();
+        }
+
+        if ($this->isValidModification($mod)) {
+            $this->modifications[] = $mod;
+
+            return $this;
+        }
+
+        throw new InvalidArgumentException(
+            "The batch modification array does not include the mandatory 'attrib' or 'modtype' keys."
+        );
+    }
+
+    /**
+     * Returns the model's distinguished name string.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function getDistinguishedName()
+    {
+        return $this->dn;
+    }
+
+    /**
+     * Sets the model's distinguished name attribute.
+     *
+     * @param string|DistinguishedName $dn
+     *
+     * @return $this
+     */
+    public function setDistinguishedName($dn)
+    {
+        $this->dn = (string) $dn;
+
+        return $this;
+    }
+
+    /**
+     * Returns the model's distinguished name string.
+     *
+     * (Alias for getDistinguishedName())
+     *
+     * @link https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function getDn()
+    {
+        return $this->getDistinguishedName();
+    }
+
+    /**
+     * Returns a DistinguishedName object for modifying the current models DN.
+     *
+     * @return DistinguishedName
+     */
+    public function getDnBuilder()
+    {
+        // If we currently don't have a distinguished name, we'll set it to our base.
+        $dn = $this->getDistinguishedName() ?: $this->query->getDn();
+
+        return $this->getNewDnBuilder($dn);
+    }
+
+    /**
+     * Returns a new DistinguishedName object for building onto.
+     *
+     * @param string $baseDn
+     *
+     * @return DistinguishedName
+     */
+    public function getNewDnBuilder($baseDn = '')
+    {
+        return new DistinguishedName($baseDn, $this->schema);
+    }
+
+    /**
+     *  Sets the model's distinguished name attribute.
+     *
+     * (Alias for setDistinguishedName())
+     *
+     * @param string $dn
+     *
+     * @return $this
+     */
+    public function setDn($dn)
+    {
+        return $this->setDistinguishedName($dn);
+    }
+
+    /**
+     * Returns the model's hex object SID.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679024(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getObjectSid()
+    {
+        return $this->getFirstAttribute($this->schema->objectSid());
+    }
+
+    /**
+     * Returns the model's binary object GUID.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679021(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getObjectGuid()
+    {
+        return $this->getFirstAttribute($this->schema->objectGuid());
+    }
+
+    /**
+     * Returns the model's GUID.
+     *
+     * @return string|null
+     */
+    public function getConvertedGuid()
+    {
+        try {
+            return (string) new Guid($this->getObjectGuid());
+        } catch (InvalidArgumentException $e) {
+            return;
+        }
+    }
+
+    /**
+     * Returns the model's SID.
+     *
+     * @return string|null
+     */
+    public function getConvertedSid()
+    {
+        try {
+            return (string) new Sid($this->getObjectSid());
+        } catch (InvalidArgumentException $e) {
+            return;
+        }
+    }
+
+    /**
+     * Returns the model's common name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675449(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getCommonName()
+    {
+        return $this->getFirstAttribute($this->schema->commonName());
+    }
+
+    /**
+     * Sets the model's common name.
+     *
+     * @param string $name
+     *
+     * @return $this
+     */
+    public function setCommonName($name)
+    {
+        return $this->setFirstAttribute($this->schema->commonName(), $name);
+    }
+
+    /**
+     * Returns the model's name. An AD alias for the CN attribute.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675449(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getName()
+    {
+        return $this->getFirstAttribute($this->schema->name());
+    }
+
+    /**
+     * Sets the model's name.
+     *
+     * @param string $name
+     *
+     * @return Model
+     */
+    public function setName($name)
+    {
+        return $this->setFirstAttribute($this->schema->name(), $name);
+    }
+
+    /**
+     * Returns the model's display name.
+     *
+     * @return string
+     */
+    public function getDisplayName()
+    {
+        return $this->getFirstAttribute($this->schema->displayName());
+    }
+
+    /**
+     * Sets the model's display name.
+     *
+     * @param string $displayName
+     *
+     * @return $this
+     */
+    public function setDisplayName($displayName)
+    {
+        return $this->setFirstAttribute($this->schema->displayName(), $displayName);
+    }
+
+    /**
+     * Returns the model's samaccountname.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679635(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getAccountName()
+    {
+        return $this->getFirstAttribute($this->schema->accountName());
+    }
+
+    /**
+     * Sets the model's samaccountname.
+     *
+     * @param string $accountName
+     *
+     * @return Model
+     */
+    public function setAccountName($accountName)
+    {
+        return $this->setFirstAttribute($this->schema->accountName(), $accountName);
+    }
+
+    /**
+     * Returns the model's samaccounttype.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679637(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getAccountType()
+    {
+        return $this->getFirstAttribute($this->schema->accountType());
+    }
+
+    /**
+     * Returns the model's `whenCreated` time.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680924(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getCreatedAt()
+    {
+        return $this->getFirstAttribute($this->schema->createdAt());
+    }
+
+    /**
+     * Returns the created at time in a mysql formatted date.
+     *
+     * @return string
+     */
+    public function getCreatedAtDate()
+    {
+        return (new DateTime())->setTimestamp($this->getCreatedAtTimestamp())->format($this->dateFormat);
+    }
+
+    /**
+     * Returns the created at time in a unix timestamp format.
+     *
+     * @return float
+     */
+    public function getCreatedAtTimestamp()
+    {
+        return DateTime::createFromFormat('YmdHis.0Z', $this->getCreatedAt())->getTimestamp();
+    }
+
+    /**
+     * Returns the model's `whenChanged` time.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680921(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getUpdatedAt()
+    {
+        return $this->getFirstAttribute($this->schema->updatedAt());
+    }
+
+    /**
+     * Returns the updated at time in a mysql formatted date.
+     *
+     * @return string
+     */
+    public function getUpdatedAtDate()
+    {
+        return (new DateTime())->setTimestamp($this->getUpdatedAtTimestamp())->format($this->dateFormat);
+    }
+
+    /**
+     * Returns the updated at time in a unix timestamp format.
+     *
+     * @return float
+     */
+    public function getUpdatedAtTimestamp()
+    {
+        return DateTime::createFromFormat($this->timestampFormat, $this->getUpdatedAt())->getTimestamp();
+    }
+
+    /**
+     * Returns the Container of the current Model.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679012(v=vs.85).aspx
+     *
+     * @return Container|Entry|bool
+     */
+    public function getObjectClass()
+    {
+        return $this->query->findByDn($this->getObjectCategoryDn());
+    }
+
+    /**
+     * Returns the CN of the model's object category.
+     *
+     * @return null|string
+     */
+    public function getObjectCategory()
+    {
+        $category = $this->getObjectCategoryArray();
+
+        if (is_array($category) && array_key_exists(0, $category)) {
+            return $category[0];
+        }
+    }
+
+    /**
+     * Returns the model's object category DN in an exploded array.
+     *
+     * @return array|false
+     */
+    public function getObjectCategoryArray()
+    {
+        return Utilities::explodeDn($this->getObjectCategoryDn());
+    }
+
+    /**
+     * Returns the model's object category DN string.
+     *
+     * @return null|string
+     */
+    public function getObjectCategoryDn()
+    {
+        return $this->getFirstAttribute($this->schema->objectCategory());
+    }
+
+    /**
+     * Returns the model's primary group ID.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679375(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrimaryGroupId()
+    {
+        return $this->getFirstAttribute($this->schema->primaryGroupId());
+    }
+
+    /**
+     * Returns the model's instance type.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676204(v=vs.85).aspx
+     *
+     * @return int
+     */
+    public function getInstanceType()
+    {
+        return $this->getFirstAttribute($this->schema->instanceType());
+    }
+
+    /**
+     * Returns the model's max password age.
+     *
+     * @return string
+     */
+    public function getMaxPasswordAge()
+    {
+        return $this->getFirstAttribute($this->schema->maxPasswordAge());
+    }
+
+    /**
+     * Returns the model's max password age in days.
+     *
+     * @return int
+     */
+    public function getMaxPasswordAgeDays()
+    {
+        $age = $this->getMaxPasswordAge();
+
+        return (int) (abs($age) / 10000000 / 60 / 60 / 24);
+    }
+
+    /**
+     * Determine if the current model is located inside the given OU.
+     *
+     * If a model instance is given, the strict parameter is ignored.
+     *
+     * @param Model|string $ou     The organizational unit to check.
+     * @param bool         $strict Whether the check is case-sensitive.
+     *
+     * @return bool
+     */
+    public function inOu($ou, $strict = false)
+    {
+        if ($ou instanceof Model) {
+            // If we've been given an OU model, we can
+            // just check if the OU's DN is inside
+            // the current models DN.
+            return (bool) strpos($this->getDn(), $ou->getDn());
+        }
+
+        $suffix = $strict ? '' : 'i';
+
+        return (bool) preg_grep("/{$ou}/{$suffix}", $this->getDnBuilder()->organizationUnits);
+    }
+
+    /**
+     * Returns true / false if the current model is writable
+     * by checking its instance type integer.
+     *
+     * @return bool
+     */
+    public function isWritable()
+    {
+        return (int) $this->getInstanceType() === 4;
+    }
+
+    /**
+     * Saves the changes to LDAP and returns the results.
+     *
+     * @param array $attributes
+     *
+     * @return bool
+     */
+    public function save(array $attributes = [])
+    {
+        return $this->exists ? $this->update($attributes) : $this->create($attributes);
+    }
+
+    /**
+     * Updates the model.
+     *
+     * @param array $attributes
+     *
+     * @return bool
+     */
+    public function update(array $attributes = [])
+    {
+        $this->fill($attributes);
+
+        $modifications = $this->getModifications();
+
+        if (count($modifications) > 0) {
+            // Push the update.
+            if ($this->query->getConnection()->modifyBatch($this->getDn(), $modifications)) {
+                // Re-sync attributes.
+                $this->syncRaw();
+
+                // Re-set the models modifications.
+                $this->modifications = [];
+
+                return true;
+            }
+
+            // Modification failed, return false.
+            return false;
+        }
+
+        // We need to return true here because modify batch will
+        // return false if no modifications are made
+        // but this may not always be the case.
+        return true;
+    }
+
+    /**
+     * Creates the entry in LDAP.
+     *
+     * @param array $attributes
+     *
+     * @return bool
+     */
+    public function create(array $attributes = [])
+    {
+        $this->fill($attributes);
+
+        if (empty($this->getDn())) {
+            // If the model doesn't currently have a DN,
+            // we'll create a new one automatically.
+            $dn = $this->getDnBuilder();
+
+            // Then we'll add the entry's common name attribute.
+            $dn->addCn($this->getCommonName());
+
+            // Set the new DN.
+            $this->setDn($dn);
+        }
+
+        // Create the entry.
+        $created = $this->query->getConnection()->add($this->getDn(), $this->getAttributes());
+
+        if ($created) {
+            // If the entry was created we'll re-sync
+            // the models attributes from AD.
+            $this->syncRaw();
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates an attribute on the current model.
+     *
+     * @param string $attribute The attribute to create
+     * @param mixed  $value     The value of the new attribute
+     * @param bool   $sync      Whether to re-sync all attributes
+     *
+     * @return bool
+     */
+    public function createAttribute($attribute, $value, $sync = true)
+    {
+        if (
+            $this->exists &&
+            $this->query->getConnection()->modAdd($this->getDn(), [$attribute => $value])
+        ) {
+            if ($sync) {
+                $this->syncRaw();
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Updates the specified attribute with the specified value.
+     *
+     * @param string $attribute The attribute to modify
+     * @param mixed  $value     The new value for the attribute
+     * @param bool   $sync      Whether to re-sync all attributes
+     *
+     * @return bool
+     */
+    public function updateAttribute($attribute, $value, $sync = true)
+    {
+        if (
+            $this->exists &&
+            $this->query->getConnection()->modReplace($this->getDn(), [$attribute => $value])
+        ) {
+            if ($sync) {
+                $this->syncRaw();
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Deletes an attribute on the current entry.
+     *
+     * @param string|array $attributes The attribute(s) to delete
+     * @param bool         $sync       Whether to re-sync all attributes
+     *
+     * Delete specific values in attributes:
+     *
+     *     ["memberuid" => "username"]
+     * 
+     * Delete an entire attribute:
+     *
+     *     ["memberuid" => []]
+     *
+     * @return bool
+     */
+    public function deleteAttribute($attributes, $sync = true)
+    {
+        // If we've been given a string, we'll assume we're removing a
+        // single attribute. Otherwise, we'll assume it's
+        // an array of attributes to remove.
+        $attributes = is_string($attributes) ? [$attributes => []] : $attributes;
+
+        if (
+            $this->exists &&
+            $this->query->getConnection()->modDelete($this->getDn(), $attributes)
+        ) {
+            if ($sync) {
+                $this->syncRaw();
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Deletes the current entry.
+     *
+     * Throws a ModelNotFoundException if the current model does
+     * not exist or does not contain a distinguished name.
+     *
+     * @throws ModelDoesNotExistException
+     *
+     * @return bool
+     */
+    public function delete()
+    {
+        $dn = $this->getDn();
+
+        if ($this->exists === false || empty($dn)) {
+            // Make sure the record exists before we can delete it.
+            // Otherwise, we'll throw an exception.
+            throw (new ModelDoesNotExistException())->setModel(get_class($this));
+        }
+
+        if ($this->query->getConnection()->delete($dn)) {
+            // We'll set the exists property to false on delete
+            // so the dev can run create operations.
+            $this->exists = false;
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Moves the current model to a new RDN and new parent.
+     *
+     * @param string      $rdn
+     * @param string|null $newParentDn
+     * @param bool|true   $deleteOldRdn
+     *
+     * @return bool
+     */
+    public function move($rdn, $newParentDn = null, $deleteOldRdn = true)
+    {
+        $moved = $this->query->getConnection()->rename($this->getDn(), $rdn, $newParentDn, $deleteOldRdn);
+
+        if ($moved) {
+            // If the model was successfully moved, we'll set its
+            // new DN so we can sync it's attributes properly.
+            $this->setDn("{$rdn},{$newParentDn}");
+
+            $this->syncRaw();
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Alias for the move method.
+     *
+     * @param string $rdn
+     *
+     * @return bool
+     */
+    public function rename($rdn)
+    {
+        return $this->move($rdn);
+    }
+
+    /**
+     * Determines if the given modification is valid.
+     *
+     * @param mixed $mod
+     *
+     * @return bool
+     */
+    protected function isValidModification($mod)
+    {
+        return is_array($mod) &&
+            array_key_exists(BatchModification::KEY_MODTYPE, $mod) &&
+            array_key_exists(BatchModification::KEY_ATTRIB, $mod);
+    }
+
+    /**
+     * Builds the models modifications from its dirty attributes.
+     *
+     * @return array
+     */
+    protected function buildModificationsFromDirty()
+    {
+        foreach ($this->getDirty() as $attribute => $values) {
+            // Make sure values is always an array.
+            $values = (is_array($values) ? $values : [$values]);
+
+            // Create a new modification.
+            $modification = $this->newBatchModification($attribute, null, $values);
+
+            if (array_key_exists($attribute, $this->original)) {
+                // If the attribute we're modifying has an original value, we'll give the
+                // BatchModification object its values to automatically determine
+                // which type of LDAP operation we need to perform.
+                $modification->setOriginal($this->original[$attribute]);
+            }
+
+            // Build the modification from its
+            // possible original values.
+            $modification->build();
+
+            if ($modification->isValid()) {
+                // Finally, we'll add the modification to the model.
+                $this->addModification($modification);
+            }
+        }
+
+        return $this->modifications;
+    }
+
+    /**
+     * Validates that the current LDAP connection is secure.
+     *
+     * @throws ConnectionException
+     *
+     * @return void
+     */
+    protected function validateSecureConnection()
+    {
+        $connection = $this->query->getConnection();
+
+        if (!$connection->isUsingSSL() && !$connection->isUsingTLS()) {
+            throw new ConnectionException(
+                "You must be connected to your LDAP server with TLS or SSL to perform this operation."
+            );
+        }
+    }
+    
+    /**
+     * Converts the inserted string boolean to a PHP boolean.
+     *
+     * @param string $bool
+     *
+     * @return null|bool
+     */
+    protected function convertStringToBool($bool)
+    {
+        $bool = strtoupper($bool);
+
+        if ($bool === strtoupper($this->schema->false())) {
+            return false;
+        } elseif ($bool === strtoupper($this->schema->true())) {
+            return true;
+        } else {
+            return;
+        }
+    }
+}

+ 38 - 0
api/vendor/adldap2/adldap2/src/Models/ModelDoesNotExistException.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\AdldapException;
+
+/**
+ * Class ModelDoesNotExistException
+ *
+ * Thrown when a model being saved / updated does not actually exist.
+ *
+ * @package Adldap\Models
+ */
+class ModelDoesNotExistException extends AdldapException
+{
+    /**
+     * The class name of the model that does not exist.
+     *
+     * @var string
+     */
+    protected $model;
+
+    /**
+     * Sets the model that does not exist.
+     *
+     * @param string $model
+     *
+     * @return ModelDoesNotExistException
+     */
+    public function setModel($model)
+    {
+        $this->model = $model;
+
+        $this->message = "Model [{$model}] does not exist.";
+
+        return $this;
+    }
+}

+ 47 - 0
api/vendor/adldap2/adldap2/src/Models/ModelNotFoundException.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\AdldapException;
+
+/**
+ * Class ModelNotFoundException
+ *
+ * Thrown when an LDAP record is not found.
+ *
+ * @package Adldap\Models
+ */
+class ModelNotFoundException extends AdldapException
+{
+    /**
+     * The query filter that was used.
+     *
+     * @var string
+     */
+    protected $query;
+
+    /**
+     * The base DN of the query that was used.
+     *
+     * @var string
+     */
+    protected $baseDn;
+
+    /**
+     * Sets the query that was used.
+     *
+     * @param string $query
+     * @param string $baseDn
+     *
+     * @return ModelNotFoundException
+     */
+    public function setQuery($query, $baseDn)
+    {
+        $this->query = $query;
+        $this->baseDn = $baseDn;
+
+        $this->message = "No LDAP query results for filter: [{$query}] in: [{$baseDn}]";
+
+        return $this;
+    }
+}

+ 23 - 0
api/vendor/adldap2/adldap2/src/Models/OrganizationalUnit.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Adldap\Models;
+
+/**
+ * Class OrganizationalUnit
+ *
+ * Represents an LDAP organizational unit.
+ *
+ * @package Adldap\Models
+ */
+class OrganizationalUnit extends Entry
+{
+    /**
+     * Retrieves the organization units OU attribute.
+     *
+     * @return string
+     */
+    public function getOu()
+    {
+        return $this->getFirstAttribute($this->schema->organizationalUnitShort());
+    }
+}

+ 288 - 0
api/vendor/adldap2/adldap2/src/Models/Printer.php

@@ -0,0 +1,288 @@
+<?php
+
+namespace Adldap\Models;
+
+/**
+ * Class Printer
+ *
+ * Represents an LDAP printer.
+ *
+ * @package Adldap\Models
+ */
+class Printer extends Entry
+{
+    /**
+     * Returns the printers name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679385(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrinterName()
+    {
+        return $this->getFirstAttribute($this->schema->printerName());
+    }
+
+    /**
+     * Returns the printers share name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679408(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrinterShareName()
+    {
+        return $this->getFirstAttribute($this->schema->printerShareName());
+    }
+
+    /**
+     * Returns the printers memory.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679396(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getMemory()
+    {
+        return $this->getFirstAttribute($this->schema->printerMemory());
+    }
+
+    /**
+     * Returns the printers URL.
+     *
+     * @return string
+     */
+    public function getUrl()
+    {
+        return $this->getFirstAttribute($this->schema->url());
+    }
+
+    /**
+     * Returns the printers location.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676839(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getLocation()
+    {
+        return $this->getFirstAttribute($this->schema->location());
+    }
+
+    /**
+     * Returns the server name that the
+     * current printer is connected to.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679772(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getServerName()
+    {
+        return $this->getFirstAttribute($this->schema->serverName());
+    }
+
+    /**
+     * Returns true / false if the printer can print in color.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679382(v=vs.85).aspx
+     *
+     * @return null|bool
+     */
+    public function getColorSupported()
+    {
+        return $this->convertStringToBool(
+            $this->getFirstAttribute(
+                $this->schema->printerColorSupported()
+            )
+        );
+    }
+
+    /**
+     * Returns true / false if the printer supports duplex printing.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679383(v=vs.85).aspx
+     *
+     * @return null|bool
+     */
+    public function getDuplexSupported()
+    {
+        return $this->convertStringToBool(
+            $this->getFirstAttribute(
+                $this->schema->printerDuplexSupported()
+            )
+        );
+    }
+
+    /**
+     * Returns an array of printer paper types that the printer supports.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679395(v=vs.85).aspx
+     *
+     * @return array
+     */
+    public function getMediaSupported()
+    {
+        return $this->getAttribute($this->schema->printerMediaSupported());
+    }
+
+    /**
+     * Returns true / false if the printer supports stapling.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679410(v=vs.85).aspx
+     *
+     * @return null|bool
+     */
+    public function getStaplingSupported()
+    {
+        return $this->convertStringToBool(
+            $this->getFirstAttribute(
+                $this->schema->printerStaplingSupported()
+            )
+        );
+    }
+
+    /**
+     * Returns an array of the printers bin names.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679380(v=vs.85).aspx
+     *
+     * @return array
+     */
+    public function getPrintBinNames()
+    {
+        return $this->getAttribute($this->schema->printerBinNames());
+    }
+
+    /**
+     * Returns the printers maximum resolution.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679391(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintMaxResolution()
+    {
+        return $this->getFirstAttribute($this->schema->printerMaxResolutionSupported());
+    }
+
+    /**
+     * Returns the printers orientations supported.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679402(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintOrientations()
+    {
+        return $this->getFirstAttribute($this->schema->printerOrientationSupported());
+    }
+
+    /**
+     * Returns the driver name of the printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675652(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getDriverName()
+    {
+        return $this->getFirstAttribute($this->schema->driverName());
+    }
+
+    /**
+     * Returns the printer drivers version number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675653(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getDriverVersion()
+    {
+        return $this->getFirstAttribute($this->schema->driverVersion());
+    }
+
+    /**
+     * Returns the priority number of the printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679413(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPriority()
+    {
+        return $this->getFirstAttribute($this->schema->priority());
+    }
+
+    /**
+     * Returns the printers start time.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679411(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintStartTime()
+    {
+        return $this->getFirstAttribute($this->schema->printerStartTime());
+    }
+
+    /**
+     * Returns the printers end time.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679384(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintEndTime()
+    {
+        return $this->getFirstAttribute($this->schema->printerEndTime());
+    }
+
+    /**
+     * Returns the port name of printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679131(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPortName()
+    {
+        return $this->getFirstAttribute($this->schema->portName());
+    }
+
+    /**
+     * Returns the printers version number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680897(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getVersionNumber()
+    {
+        return $this->getFirstAttribute($this->schema->versionNumber());
+    }
+
+    /**
+     * Returns the print rate.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679405(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintRate()
+    {
+        return $this->getFirstAttribute($this->schema->printerPrintRate());
+    }
+
+    /**
+     * Returns the print rate unit.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679406(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getPrintRateUnit()
+    {
+        return $this->getFirstAttribute($this->schema->printerPrintRate());
+    }
+}

+ 90 - 0
api/vendor/adldap2/adldap2/src/Models/RootDse.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace Adldap\Models;
+
+use DateTime;
+
+/**
+ * Class RootDse
+ *
+ * Represents the LDAP connections Root DSE record.
+ *
+ * @package Adldap\Models
+ */
+class RootDse extends Model
+{
+    /**
+     * Returns the hosts current time in unix timestamp format.
+     *
+     * @return int
+     */
+    public function getCurrentTime()
+    {
+        $time = $this->getFirstAttribute($this->schema->currentTime());
+
+        return DateTime::createFromFormat($this->timestampFormat, $time)
+            ->getTimestamp();
+    }
+
+    /**
+     * Returns the hosts current time in the models date format.
+     *
+     * @return string
+     */
+    public function getCurrentTimeDate()
+    {
+        return (new DateTime())
+            ->setTimestamp($this->getCurrentTime())
+            ->format($this->dateFormat);
+    }
+
+    /**
+     * Returns the hosts configuration naming context.
+     *
+     * @return string
+     */
+    public function getConfigurationNamingContext()
+    {
+        return $this->getFirstAttribute($this->schema->configurationNamingContext());
+    }
+
+    /**
+     * Returns the hosts schema naming context.
+     *
+     * @return string
+     */
+    public function getSchemaNamingContext()
+    {
+        return $this->getFirstAttribute($this->schema->schemaNamingContext());
+    }
+
+    /**
+     * Returns the hosts DNS name.
+     *
+     * @return string
+     */
+    public function getDnsHostName()
+    {
+        return $this->getFirstAttribute($this->schema->dnsHostName());
+    }
+
+    /**
+     * Returns the current hosts server name.
+     *
+     * @return string
+     */
+    public function getServerName()
+    {
+        return $this->getFirstAttribute($this->schema->serverName());
+    }
+
+    /**
+     * Returns the DN of the root domain NC for this DC's forest.
+     *
+     * @return mixed
+     */
+    public function getRootDomainNamingContext()
+    {
+        return $this->getFirstAttribute($this->schema->rootDomainNamingContext());
+    }
+}

+ 1152 - 0
api/vendor/adldap2/adldap2/src/Models/User.php

@@ -0,0 +1,1152 @@
+<?php
+
+namespace Adldap\Models;
+
+use DateTime;
+use Adldap\Utilities;
+use Adldap\AdldapException;
+use Adldap\Models\Concerns\HasMemberOf;
+use Adldap\Models\Concerns\HasDescription;
+use Adldap\Models\Concerns\HasUserAccountControl;
+use Adldap\Models\Concerns\HasLastLogonAndLogOff;
+use Illuminate\Contracts\Auth\Authenticatable;
+
+/**
+ * Class User
+ *
+ * Represents an LDAP user.
+ *
+ * @package Adldap\Models
+ */
+class User extends Entry implements Authenticatable
+{
+    use HasDescription,
+        HasMemberOf,
+        HasLastLogonAndLogOff,
+        HasUserAccountControl;
+
+    /**
+     * Get the name of the unique identifier for the user.
+     *
+     * @return string
+     */
+    public function getAuthIdentifierName()
+    {
+        return $this->schema->objectGuid();
+    }
+
+    /**
+     * Get the unique identifier for the user.
+     *
+     * @return mixed
+     */
+    public function getAuthIdentifier()
+    {
+        return $this->getConvertedGuid();
+    }
+
+    /**
+     * Get the password for the user.
+     *
+     * @return string
+     */
+    public function getAuthPassword()
+    {
+        return;
+    }
+
+    /**
+     * Get the token value for the "remember me" session.
+     *
+     * @return string
+     */
+    public function getRememberToken()
+    {
+        return;
+    }
+
+    /**
+     * Set the token value for the "remember me" session.
+     *
+     * @param string $value
+     *
+     * @return void
+     */
+    public function setRememberToken($value)
+    {
+        return;
+    }
+
+    /**
+     * Get the column name for the "remember me" token.
+     *
+     * @return string
+     */
+    public function getRememberTokenName()
+    {
+        return;
+    }
+
+    /**
+     * Returns the users department.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675490(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getDepartment()
+    {
+        return $this->getFirstAttribute($this->schema->department());
+    }
+
+    /**
+     * Sets the users department.
+     *
+     * @param string $department
+     *
+     * @return $this
+     */
+    public function setDepartment($department)
+    {
+        return $this->setFirstAttribute($this->schema->department(), $department);
+    }
+
+    /**
+     * Returns the department number.
+     *
+     * @return string
+     */
+    public function getDepartmentNumber()
+    {
+        return $this->getFirstAttribute($this->schema->departmentNumber());
+    }
+
+    /**
+     * Sets the department number.
+     *
+     * @param string $number
+     *
+     * @return $this
+     */
+    public function setDepartmentNumber($number)
+    {
+        return $this->setFirstAttribute($this->schema->departmentNumber(), $number);
+    }
+
+    /**
+     * Returns the users title.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680037(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->getFirstAttribute($this->schema->title());
+    }
+
+    /**
+     * Sets the users title.
+     *
+     * @param string $title
+     *
+     * @return $this
+     */
+    public function setTitle($title)
+    {
+        return $this->setFirstAttribute($this->schema->title(), $title);
+    }
+
+    /**
+     * Returns the users first name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675719(v=vs.85).aspx
+     *
+     * @return mixed
+     */
+    public function getFirstName()
+    {
+        return $this->getFirstAttribute($this->schema->firstName());
+    }
+
+    /**
+     * Sets the users first name.
+     *
+     * @param string $firstName
+     *
+     * @return $this
+     */
+    public function setFirstName($firstName)
+    {
+        return $this->setFirstAttribute($this->schema->firstName(), $firstName);
+    }
+
+    /**
+     * Returns the users last name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679872(v=vs.85).aspx
+     *
+     * @return mixed
+     */
+    public function getLastName()
+    {
+        return $this->getFirstAttribute($this->schema->lastName());
+    }
+
+    /**
+     * Sets the users last name.
+     *
+     * @param string $lastName
+     *
+     * @return $this
+     */
+    public function setLastName($lastName)
+    {
+        return $this->setFirstAttribute($this->schema->lastName(), $lastName);
+    }
+
+    /**
+     * Returns the users info.
+     *
+     * @return mixed
+     */
+    public function getInfo()
+    {
+        return $this->getFirstAttribute($this->schema->info());
+    }
+
+    /**
+     * Sets the users info.
+     *
+     * @param string $info
+     *
+     * @return $this
+     */
+    public function setInfo($info)
+    {
+        return $this->setFirstAttribute($this->schema->info(), $info);
+    }
+
+    /**
+     * Returns the users initials.
+     *
+     * @return mixed
+     */
+    public function getInitials()
+    {
+        return $this->getFirstAttribute($this->schema->initials());
+    }
+
+    /**
+     * Sets the users initials.
+     *
+     * @param string $initials
+     *
+     * @return $this
+     */
+    public function setInitials($initials)
+    {
+        return $this->setFirstAttribute($this->schema->initials(), $initials);
+    }
+
+    /**
+     * Returns the users country.
+     *
+     * @return string
+     */
+    public function getCountry()
+    {
+        return $this->getFirstAttribute($this->schema->country());
+    }
+
+    /**
+     * Sets the users country.
+     *
+     * @param string $country
+     *
+     * @return $this
+     */
+    public function setCountry($country)
+    {
+        return $this->setFirstAttribute($this->schema->country(), $country);
+    }
+
+    /**
+     * Returns the users street address.
+     *
+     * @return $this
+     */
+    public function getStreetAddress()
+    {
+        return $this->getFirstAttribute($this->schema->streetAddress());
+    }
+
+    /**
+     * Sets the users street address.
+     *
+     * @param string $address
+     *
+     * @return $this
+     */
+    public function setStreetAddress($address)
+    {
+        return $this->setFirstAttribute($this->schema->streetAddress(), $address);
+    }
+
+    /**
+     * Returns the users postal code.
+     *
+     * @return string
+     */
+    public function getPostalCode()
+    {
+        return $this->getFirstAttribute($this->schema->postalCode());
+    }
+
+    /**
+     * Sets the users postal code.
+     *
+     * @param string $postalCode
+     *
+     * @return $this
+     */
+    public function setPostalCode($postalCode)
+    {
+        return $this->setFirstAttribute($this->schema->postalCode(), $postalCode);
+    }
+
+    /**
+     * Returns the users physical delivery office name.
+     *
+     * @return string
+     */
+    public function getPhysicalDeliveryOfficeName()
+    {
+        return $this->getFirstAttribute($this->schema->physicalDeliveryOfficeName());
+    }
+
+    /**
+     * Sets the users physical delivery office name.
+     *
+     * @param string $deliveryOffice
+     *
+     * @return $this
+     */
+    public function setPhysicalDeliveryOfficeName($deliveryOffice)
+    {
+        return $this->setFirstAttribute($this->schema->physicalDeliveryOfficeName(), $deliveryOffice);
+    }
+
+    /**
+     * Returns the users telephone number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680027(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getTelephoneNumber()
+    {
+        return $this->getFirstAttribute($this->schema->telephone());
+    }
+
+    /**
+     * Sets the users telephone number.
+     *
+     * @param string $number
+     *
+     * @return $this
+     */
+    public function setTelephoneNumber($number)
+    {
+        return $this->setFirstAttribute($this->schema->telephone(), $number);
+    }
+
+    /**
+     * Returns the users locale.
+     *
+     * @return string
+     */
+    public function getLocale()
+    {
+        return $this->getFirstAttribute($this->schema->locale());
+    }
+
+    /**
+     * Sets the users locale.
+     *
+     * @param string $locale
+     *
+     * @return $this
+     */
+    public function setLocale($locale)
+    {
+        return $this->setFirstAttribute($this->schema->locale(), $locale);
+    }
+
+    /**
+     * Returns the users company.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675457(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getCompany()
+    {
+        return $this->getFirstAttribute($this->schema->company());
+    }
+
+    /**
+     * Sets the users company.
+     *
+     * @param string $company
+     *
+     * @return $this
+     */
+    public function setCompany($company)
+    {
+        return $this->setFirstAttribute($this->schema->company(), $company);
+    }
+
+    /**
+     * Returns the users primary email address.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676855(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getEmail()
+    {
+        return $this->getFirstAttribute($this->schema->email());
+    }
+
+    /**
+     * Sets the users email.
+     *
+     * Keep in mind this will remove all other
+     * email addresses the user currently has.
+     *
+     * @param string $email
+     *
+     * @return $this
+     */
+    public function setEmail($email)
+    {
+        return $this->setFirstAttribute($this->schema->email(), $email);
+    }
+
+    /**
+     * Returns the users email addresses.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676855(v=vs.85).aspx
+     *
+     * @return array
+     */
+    public function getEmails()
+    {
+        return $this->getAttribute($this->schema->email());
+    }
+
+    /**
+     * Sets the users email addresses.
+     *
+     * @param array $emails
+     *
+     * @return $this
+     */
+    public function setEmails(array $emails = [])
+    {
+        return $this->setAttribute($this->schema->email(), $emails);
+    }
+
+    /**
+     * Returns the users other mailbox attribute.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679091(v=vs.85).aspx
+     *
+     * @return array
+     */
+    public function getOtherMailbox()
+    {
+        return $this->getAttribute($this->schema->otherMailbox());
+    }
+
+    /**
+     * Sets the users other mailboxes.
+     *
+     * @param array $otherMailbox
+     *
+     * @return $this
+     */
+    public function setOtherMailbox($otherMailbox = [])
+    {
+        return $this->setAttribute($this->schema->otherMailbox(), $otherMailbox);
+    }
+
+    /**
+     * Returns the users mailbox store DN.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/aa487565(v=exchg.65).aspx
+     *
+     * @return string
+     */
+    public function getHomeMdb()
+    {
+        return $this->getFirstAttribute($this->schema->homeMdb());
+    }
+
+    /**
+     * Sets the users home drive.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676191(v=vs.85).aspx
+     * 
+     * @return $this
+     */
+    public function setHomeDrive($drive)
+    {
+        return $this->setAttribute($this->schema->homeDrive(), $drive);
+    }
+
+    /**
+     * Specifies the drive letter to which to map the UNC path specified by homeDirectory.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676191(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function getHomeDrive()
+    {
+        return $this->getFirstAttribute($this->schema->homeDrive());
+    }
+
+    /**
+     * Sets the users home directory.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676190(v=vs.85).aspx
+     * 
+     * @return $this
+     */
+    public function setHomeDirectory($directory)
+    {
+        return $this->setAttribute($this->schema->homeDirectory(), $directory);
+    }
+
+    /**
+     * The home directory for the account.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676190(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function getHomeDirectory()
+    {
+        return $this->getFirstAttribute($this->schema->homeDirectory());
+    }
+
+    /**
+     * Returns the users mail nickname.
+     *
+     * @return string
+     */
+    public function getMailNickname()
+    {
+        return $this->getFirstAttribute($this->schema->emailNickname());
+    }
+
+    /**
+     * Returns the users principal name.
+     *
+     * This is usually their email address.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680857(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getUserPrincipalName()
+    {
+        return $this->getFirstAttribute($this->schema->userPrincipalName());
+    }
+
+    /**
+     * Sets the users user principal name.
+     *
+     * @param string $userPrincipalName
+     *
+     * @return $this
+     */
+    public function setUserPrincipalName($userPrincipalName)
+    {
+        return $this->setFirstAttribute($this->schema->userPrincipalName(), $userPrincipalName);
+    }
+
+    /**
+     * Returns the users proxy addresses.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx
+     *
+     * @return array
+     */
+    public function getProxyAddresses()
+    {
+        return $this->getAttribute($this->schema->proxyAddresses());
+    }
+
+    /**
+     * Sets the users proxy addresses.
+     *
+     * This will remove all proxy addresses on the user and insert the specified addresses.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx
+     *
+     * @param array $addresses
+     *
+     * @return $this
+     */
+    public function setProxyAddresses(array $addresses = [])
+    {
+        return $this->setAttribute($this->schema->proxyAddresses(), $addresses);
+    }
+
+    /**
+     * Add's a single proxy address to the user.
+     *
+     * @param string $address
+     *
+     * @return $this
+     */
+    public function addProxyAddress($address)
+    {
+        $addresses = $this->getProxyAddresses();
+
+        $addresses[] = $address;
+
+        return $this->setAttribute($this->schema->proxyAddresses(), $addresses);
+    }
+
+    /**
+     * Returns the users script path if the user has one.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679656(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function getScriptPath()
+    {
+        return $this->getFirstAttribute($this->schema->scriptPath());
+    }
+
+    /**
+     * Sets the users script path.
+     *
+     * @param string $path
+     *
+     * @return $this
+     */
+    public function setScriptPath($path)
+    {
+        return $this->setFirstAttribute($this->schema->scriptPath(), $path);
+    }
+
+    /**
+     * Returns the users bad password count.
+     *
+     * @return string
+     */
+    public function getBadPasswordCount()
+    {
+        return $this->getFirstAttribute($this->schema->badPasswordCount());
+    }
+
+    /**
+     * Returns the users bad password time.
+     *
+     * @return string
+     */
+    public function getBadPasswordTime()
+    {
+        return $this->getFirstAttribute($this->schema->badPasswordTime());
+    }
+
+    /**
+     * Returns the time when the users password was set last.
+     *
+     * @return string
+     */
+    public function getPasswordLastSet()
+    {
+        return $this->getFirstAttribute($this->schema->passwordLastSet());
+    }
+
+    /**
+     * Returns the password last set unix timestamp.
+     *
+     * @return float|null
+     */
+    public function getPasswordLastSetTimestamp()
+    {
+        if ($time = $this->getPasswordLastSet()) {
+            return Utilities::convertWindowsTimeToUnixTime($time);
+        }
+    }
+
+    /**
+     * Returns the formatted timestamp of the password last set date.
+     *
+     * @return string|null
+     */
+    public function getPasswordLastSetDate()
+    {
+        if ($timestamp = $this->getPasswordLastSetTimestamp()) {
+            return (new DateTime())->setTimestamp($timestamp)->format($this->dateFormat);
+        }
+    }
+
+    /**
+     * Returns the users lockout time.
+     *
+     * @return string
+     */
+    public function getLockoutTime()
+    {
+        return $this->getFirstAttribute($this->schema->lockoutTime());
+    }
+
+    /**
+     * Returns the users profile file path.
+     *
+     * @return string
+     */
+    public function getProfilePath()
+    {
+        return $this->getFirstAttribute($this->schema->profilePath());
+    }
+
+    /**
+     * Sets the users profile path.
+     *
+     * @param string $path
+     *
+     * @return $this
+     */
+    public function setProfilePath($path)
+    {
+        return $this->setFirstAttribute($this->schema->profilePath(), $path);
+    }
+
+    /**
+     * Returns the users legacy exchange distinguished name.
+     *
+     * @return string
+     */
+    public function getLegacyExchangeDn()
+    {
+        return $this->getFirstAttribute($this->schema->legacyExchangeDn());
+    }
+
+    /**
+     * Returns the users account expiry date.
+     *
+     * @return string
+     */
+    public function getAccountExpiry()
+    {
+        return $this->getFirstAttribute($this->schema->accountExpires());
+    }
+
+    /**
+     * Sets the users account expiry date.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
+     *
+     * @param float $expiryTime
+     *
+     * @return $this
+     */
+    public function setAccountExpiry($expiryTime)
+    {
+        $time = is_null($expiryTime) ? '9223372036854775807' : (string) Utilities::convertUnixTimeToWindowsTime($expiryTime);
+
+        return $this->setFirstAttribute($this->schema->accountExpires(), $time);
+    }
+
+    /**
+     * Returns an array of address book DNs
+     * that the user is listed to be shown in.
+     *
+     * @return array
+     */
+    public function getShowInAddressBook()
+    {
+        return $this->getAttribute($this->schema->showInAddressBook());
+    }
+
+    /**
+     * Returns the users thumbnail photo.
+     *
+     * @return mixed
+     */
+    public function getThumbnail()
+    {
+        return $this->getFirstAttribute($this->schema->thumbnail());
+    }
+
+    /**
+     * Returns the users thumbnail photo base 64 encoded.
+     *
+     * Suitable for inserting into an HTML image element.
+     *
+     * @return null|string
+     */
+    public function getThumbnailEncoded()
+    {
+        $data = base64_decode($this->getThumbnail());
+
+        if ($data) {
+            // In case we don't have the file info extension enabled,
+            // we'll set the jpeg mime type as default.
+            $mime = 'image/jpeg';
+
+            $image = base64_encode($data);
+
+            if (function_exists('finfo_open')) {
+                $finfo = finfo_open();
+
+                $mime = finfo_buffer($finfo, $data, FILEINFO_MIME_TYPE);
+
+                return "data:$mime;base64,$image";
+            }
+
+            return "data:$mime;base64,$image";
+        }
+    }
+
+    /**
+     * Sets the users thumbnail photo.
+     *
+     * @param string $data
+     * @param bool   $encode
+     *
+     * @return $this
+     */
+    public function setThumbnail($data, $encode = true)
+    {
+        if ($encode && !base64_decode($data, $strict = true)) {
+            // If the string we're given is not base 64 encoded, then
+            // we will encode it before setting it on the user.
+            $data = base64_encode($data);
+        }
+
+        return $this->setAttribute($this->schema->thumbnail(), $data);
+    }
+
+    /**
+     * Returns the users JPEG photo.
+     *
+     * @return mixed
+     */
+    public function getJpegPhoto()
+    {
+        return $this->getFirstAttribute($this->schema->jpegPhoto());
+    }
+
+    /**
+     * Returns the users JPEG photo.
+     *
+     * @return null|string
+     */
+    public function getJpegPhotoEncoded()
+    {
+        $jpeg = $this->getJpegPhoto();
+
+        return is_null($jpeg) ? $jpeg : 'data:image/jpeg;base64,'.base64_encode($jpeg);
+    }
+
+    /**
+     * Sets the users JPEG photo.
+     *
+     * @param string $string
+     *
+     * @return $this
+     */
+    public function setJpegPhoto($string)
+    {
+        if (!base64_decode($string, $strict = true)) {
+            $string = base64_encode($string);
+        }
+
+        return $this->setAttribute($this->schema->jpegPhoto(), $string);
+    }
+
+    /**
+     * Returns the distinguished name of the user who is the user's manager.
+     *
+     * @return string
+     */
+    public function getManager()
+    {
+        return $this->getFirstAttribute($this->schema->manager());
+    }
+
+    /**
+     * Sets the distinguished name of the user who is the user's manager.
+     *
+     * @param string $managerDn
+     *
+     * @return $this
+     */
+    public function setManager($managerDn)
+    {
+        return $this->setFirstAttribute($this->schema->manager(), $managerDn);
+    }
+
+    /**
+     * Return the employee ID.
+     *
+     * @return string
+     */
+    public function getEmployeeId()
+    {
+        return $this->getFirstAttribute($this->schema->employeeId());
+    }
+
+    /**
+     * Sets the employee ID.
+     *
+     * @param string $employeeId
+     *
+     * @return $this
+     */
+    public function setEmployeeId($employeeId)
+    {
+        return $this->setFirstAttribute($this->schema->employeeId(), $employeeId);
+    }
+
+    /**
+     * Returns the employee number.
+     *
+     * @return string
+     */
+    public function getEmployeeNumber()
+    {
+        return $this->getFirstAttribute($this->schema->employeeNumber());
+    }
+
+    /**
+     * Sets the employee number.
+     *
+     * @param string $number
+     *
+     * @return $this
+     */
+    public function setEmployeeNumber($number)
+    {
+        return $this->setFirstAttribute($this->schema->employeeNumber(), $number);
+    }
+
+    /**
+     * Returns the room number.
+     *
+     * @return string
+     */
+    public function getRoomNumber()
+    {
+        return $this->getFirstAttribute($this->schema->roomNumber());
+    }
+
+    /**
+     * Sets the room number.
+     *
+     * @param string $number
+     *
+     * @return $this
+     */
+    public function setRoomNumber($number)
+    {
+        return $this->setFirstAttribute($this->schema->roomNumber(), $number);
+    }
+
+    /**
+     * Return the personal title.
+     *
+     * @return $this
+     */
+    public function getPersonalTitle()
+    {
+        return $this->getFirstAttribute($this->schema->personalTitle());
+    }
+
+    /**
+     * Sets the personal title.
+     *
+     * @param string $personalTitle
+     *
+     * @return $this
+     */
+    public function setPersonalTitle($personalTitle)
+    {
+        return $this->setFirstAttribute($this->schema->personalTitle(), $personalTitle);
+    }
+
+    /**
+     * Retrieves the primary group of the current user.
+     *
+     * @return Model|bool
+     */
+    public function getPrimaryGroup()
+    {
+        $groupSid = preg_replace('/\d+$/', $this->getPrimaryGroupId(), $this->getConvertedSid());
+
+        return $this->query->newInstance()->findBySid($groupSid);
+    }
+
+    /**
+     * Sets the password on the current user.
+     *
+     * @param string $password
+     *
+     * @throws AdldapException When no SSL or TLS secured connection is present.
+     *
+     * @return $this
+     */
+    public function setPassword($password)
+    {
+        $this->validateSecureConnection();
+
+        $mod = $this->newBatchModification(
+            $this->schema->unicodePassword(),
+            LDAP_MODIFY_BATCH_REPLACE,
+            [Utilities::encodePassword($password)]
+        );
+
+        return $this->addModification($mod);
+    }
+
+    /**
+     * Change the password of the current user. This must be performed over SSL / TLS.
+     *
+     * Throws an exception on failure.
+     *
+     * @param string $oldPassword      The new password
+     * @param string $newPassword      The old password
+     * @param bool   $replaceNotRemove Alternative password change method. Set to true if you're receiving 'CONSTRAINT'
+     *                                 errors.
+     *
+     * @throws UserPasswordPolicyException When the new password does not match your password policy.
+     * @throws UserPasswordIncorrectException When the old password is incorrect.
+     * @throws AdldapException When an unknown cause of failure occurs.
+     *
+     * @return true
+     */
+    public function changePassword($oldPassword, $newPassword, $replaceNotRemove = false)
+    {
+        $this->validateSecureConnection();
+
+        $attribute = $this->schema->unicodePassword();
+
+        $modifications = [];
+
+        if ($replaceNotRemove) {
+            $modifications[] = $this->newBatchModification(
+                $attribute,
+                LDAP_MODIFY_BATCH_REPLACE,
+                [Utilities::encodePassword($newPassword)]
+            );
+        } else {
+            // Create batch modification for removing the old password.
+            $modifications[] = $this->newBatchModification(
+                $attribute,
+                LDAP_MODIFY_BATCH_REMOVE,
+                [Utilities::encodePassword($oldPassword)]
+            );
+
+            // Create batch modification for adding the new password.
+            $modifications[] = $this->newBatchModification(
+                $attribute,
+                LDAP_MODIFY_BATCH_ADD,
+                [Utilities::encodePassword($newPassword)]
+            );
+        }
+
+        // Add the modifications.
+        foreach ($modifications as $modification) {
+            $this->addModification($modification);
+        }
+
+        $result = @$this->update();
+
+        if (!$result) {
+            // If the user failed to update, we'll see if we can
+            // figure out why by retrieving the extended error.
+            $error = $this->query->getConnection()->getExtendedError();
+            $code = $this->query->getConnection()->getExtendedErrorCode();
+
+            switch ($code) {
+                case '0000052D':
+                    throw new UserPasswordPolicyException(
+                        "Error: $code. Your new password does not match the password policy."
+                    );
+                case '00000056':
+                    throw new UserPasswordIncorrectException(
+                        "Error: $code. Your old password is incorrect."
+                    );
+                default:
+                    throw new AdldapException($error);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Return the expiration date of the user account.
+     *
+     * @return DateTime|null
+     */
+    public function expirationDate()
+    {
+        $accountExpiry = $this->getAccountExpiry();
+
+        if ($accountExpiry == 0 || $accountExpiry == $this->getSchema()->neverExpiresDate()) {
+            return;
+        }
+
+        $unixTime = Utilities::convertWindowsTimeToUnixTime($accountExpiry);
+
+        return new DateTime(date($this->dateFormat, $unixTime));
+    }
+
+    /**
+     * Return true / false if the AD User is expired.
+     *
+     * @param DateTime $date Optional date
+     *
+     * @return bool
+     */
+    public function isExpired(DateTime $date = null)
+    {
+        $date = $date ?: new DateTime();
+
+        $expirationDate = $this->expirationDate();
+
+        return $expirationDate ? ($expirationDate <= $date) : false;
+    }
+
+    /**
+     * Return true / false if AD User is active (enabled & not expired).
+     *
+     * @return bool
+     */
+    public function isActive()
+    {
+        return $this->isEnabled() && !$this->isExpired();
+    }
+
+    /**
+     * Returns true / false if the users password is expired.
+     *
+     * @return bool
+     */
+    public function passwordExpired()
+    {
+        return (int) $this->getPasswordLastSet() === 0;
+    }
+}

+ 18 - 0
api/vendor/adldap2/adldap2/src/Models/UserPasswordIncorrectException.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\AdldapException;
+
+/**
+ * Class UserPasswordIncorrectException
+ *
+ * Thrown when a users password is being changed
+ * and their current password given is incorrect.
+ *
+ * @package Adldap\Models
+ */
+class UserPasswordIncorrectException extends AdldapException
+{
+    //
+}

+ 18 - 0
api/vendor/adldap2/adldap2/src/Models/UserPasswordPolicyException.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Adldap\Models;
+
+use Adldap\AdldapException;
+
+/**
+ * Class UserPasswordPolicyException
+ *
+ * Thrown when a users password is being changed but their new password
+ * does not conform to the LDAP servers password policy.
+ *
+ * @package Adldap\Models
+ */
+class UserPasswordPolicyException extends AdldapException
+{
+    //
+}

+ 1510 - 0
api/vendor/adldap2/adldap2/src/Query/Builder.php

@@ -0,0 +1,1510 @@
+<?php
+
+namespace Adldap\Query;
+
+use Closure;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
+use InvalidArgumentException;
+use Adldap\Utilities;
+use Adldap\Models\Model;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Schemas\ActiveDirectory;
+use Adldap\Models\ModelNotFoundException;
+use Adldap\Connections\ConnectionInterface;
+
+class Builder
+{
+    /**
+     * The selected columns to retrieve on the query.
+     *
+     * @var array
+     */
+    public $columns = ['*'];
+
+    /**
+     * The query filters.
+     *
+     * @var array
+     */
+    public $filters = [
+        'and' => [],
+        'or' => [],
+        'raw' => [],
+    ];
+
+    /**
+     * The size limit of the query.
+     *
+     * @var int
+     */
+    public $limit = 0;
+
+    /**
+     * Determines whether the current query is paginated.
+     *
+     * @var bool
+     */
+    public $paginated = false;
+
+    /**
+     * The field to sort search results by.
+     *
+     * @var string
+     */
+    protected $sortByField = '';
+
+    /**
+     * The direction to sort the results by.
+     *
+     * @var string
+     */
+    protected $sortByDirection = '';
+
+    /**
+     * The sort flags for sorting query results.
+     *
+     * @var int
+     */
+    protected $sortByFlags;
+
+    /**
+     * The distinguished name to perform searches upon.
+     *
+     * @var string|null
+     */
+    protected $dn;
+
+    /**
+     * The default query type.
+     *
+     * @var string
+     */
+    protected $type = 'search';
+
+    /**
+     * Determines whether or not to return LDAP results in their raw array format.
+     *
+     * @var bool
+     */
+    protected $raw = false;
+
+    /**
+     * Determines whether the query is nested.
+     *
+     * @var bool
+     */
+    protected $nested = false;
+
+    /**
+     * The current connection instance.
+     *
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * The current grammar instance.
+     *
+     * @var Grammar
+     */
+    protected $grammar;
+
+    /**
+     * The current schema instance.
+     *
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * Constructor.
+     *
+     * @param ConnectionInterface  $connection
+     * @param Grammar|null         $grammar
+     * @param SchemaInterface|null $schema
+     */
+    public function __construct(ConnectionInterface $connection, Grammar $grammar = null, SchemaInterface $schema = null)
+    {
+        $this->setConnection($connection)
+            ->setGrammar($grammar)
+            ->setSchema($schema);
+    }
+
+    /**
+     * Sets the current connection.
+     *
+     * @param ConnectionInterface $connection
+     *
+     * @return Builder
+     */
+    public function setConnection(ConnectionInterface $connection)
+    {
+        $this->connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Sets the current filter grammar.
+     *
+     * @param Grammar|null $grammar
+     *
+     * @return Builder
+     */
+    public function setGrammar(Grammar $grammar = null)
+    {
+        $this->grammar = $grammar ?: new Grammar();
+
+        return $this;
+    }
+
+    /**
+     * Sets the current schema.
+     *
+     * @param SchemaInterface|null $schema
+     *
+     * @return Builder
+     */
+    public function setSchema(SchemaInterface $schema = null)
+    {
+        $this->schema = $schema ?: new ActiveDirectory();
+
+        return $this;
+    }
+
+    /**
+     * Returns the current schema.
+     *
+     * @return SchemaInterface
+     */
+    public function getSchema()
+    {
+        return $this->schema;
+    }
+
+    /**
+     * Returns a new Query Builder instance.
+     *
+     * @param string $baseDn
+     *
+     * @return Builder
+     */
+    public function newInstance($baseDn = null)
+    {
+        // We'll set the base DN of the new Builder so
+        // developers don't need to do this manually.
+        $dn = is_null($baseDn) ? $this->getDn() : $baseDn;
+
+        return (new static($this->connection, $this->grammar, $this->schema))
+            ->setDn($dn);
+    }
+
+    /**
+     * Returns a new nested Query Builder instance.
+     * 
+     * @param Closure|null $closure
+     * 
+     * @return $this
+     */
+    public function newNestedInstance(Closure $closure = null)
+    {
+        $query = $this->newInstance()->nested();
+        
+        if ($closure) {
+            call_user_func($closure, $query);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Returns the current query.
+     *
+     * @return \Illuminate\Support\Collection|array
+     */
+    public function get()
+    {
+        // We'll mute any warnings / errors here. We just need to
+        // know if any query results were returned.
+        return @$this->query($this->getQuery());
+    }
+
+    /**
+     * Compiles and returns the current query string.
+     *
+     * @return string
+     */
+    public function getQuery()
+    {
+        return $this->grammar->compile($this);
+    }
+
+    /**
+     * Returns the unescaped query.
+     *
+     * @return string
+     */
+    public function getUnescapedQuery()
+    {
+        return Utilities::unescape($this->getQuery());
+    }
+
+    /**
+     * Returns the current Grammar instance.
+     *
+     * @return Grammar
+     */
+    public function getGrammar()
+    {
+        return $this->grammar;
+    }
+
+    /**
+     * Returns the current Connection instance.
+     *
+     * @return ConnectionInterface
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Returns the builders DN to perform searches upon.
+     *
+     * @return string
+     */
+    public function getDn()
+    {
+        return $this->dn;
+    }
+
+    /**
+     * Sets the DN to perform searches upon.
+     *
+     * @param string|null $dn
+     *
+     * @return Builder
+     */
+    public function setDn($dn = null)
+    {
+        $this->dn = (string) $dn;
+
+        return $this;
+    }
+
+    /**
+     * Alias for setting the base DN of the query.
+     *
+     * @param string $dn
+     *
+     * @return Builder
+     */
+    public function in($dn)
+    {
+        return $this->setDn($dn);
+    }
+
+    /**
+     * Sets the size limit of the current query.
+     *
+     * @param int $limit
+     *
+     * @return Builder
+     */
+    public function limit($limit = 0)
+    {
+        $this->limit = $limit;
+
+        return $this;
+    }
+
+    /**
+     * Performs the specified query on the current LDAP connection.
+     *
+     * @param string $query
+     *
+     * @return \Illuminate\Support\Collection|array
+     */
+    public function query($query)
+    {
+        $results = $this->connection->{$this->type}(
+            $this->getDn(),
+            $query,
+            $this->getSelects(),
+            $onlyAttributes = false,
+            $this->limit
+        );
+
+        return $this->newProcessor()->process($results);
+    }
+
+    /**
+     * Paginates the current LDAP query.
+     *
+     * @param int  $perPage
+     * @param int  $currentPage
+     * @param bool $isCritical
+     *
+     * @return Paginator
+     */
+    public function paginate($perPage = 50, $currentPage = 0, $isCritical = true)
+    {
+        $this->paginated = true;
+
+        $pages = [];
+
+        $cookie = '';
+
+        do {
+            $this->connection->controlPagedResult($perPage, $isCritical, $cookie);
+
+            // Run the search.
+            $resource = @$this->connection->search($this->getDn(), $this->getQuery(), $this->getSelects());
+
+            if ($resource) {
+                $this->connection->controlPagedResultResponse($resource, $cookie);
+
+                // We'll collect each resource result into the pages array.
+                $pages[] = $resource;
+            }
+        } while (!empty($cookie));
+
+        $paginator = $this->newProcessor()->processPaginated($pages, $perPage, $currentPage);
+
+        // Reset paged result on the current connection. We won't pass in the current $perPage
+        // parameter since we want to reset the page size to the default '1000'. Sending '0'
+        // eliminates any further opportunity for running queries in the same request,
+        // even though that is supposed to be the correct usage.
+        $this->connection->controlPagedResult();
+
+        return $paginator;
+    }
+
+    /**
+     * Returns the first entry in a search result.
+     *
+     * @param array|string $columns
+     *
+     * @return Model|array|null
+     */
+    public function first($columns = [])
+    {
+        $results = $this->select($columns)->limit(1)->get();
+
+        return Arr::get($results, 0);
+    }
+
+    /**
+     * Returns the first entry in a search result.
+     *
+     * If no entry is found, an exception is thrown.
+     *
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return Model
+     */
+    public function firstOrFail($columns = [])
+    {
+        $record = $this->first($columns);
+
+        if (!$record) {
+            throw (new ModelNotFoundException())
+                ->setQuery($this->getUnescapedQuery(), $this->getDn());
+        }
+
+        return $record;
+    }
+
+    /**
+     * Finds a record by the specified attribute and value.
+     *
+     * @param string       $attribute
+     * @param string       $value
+     * @param array|string $columns
+     *
+     * @return mixed
+     */
+    public function findBy($attribute, $value, $columns = [])
+    {
+        return $this->whereEquals($attribute, $value)->first($columns);
+    }
+
+    /**
+     * Finds a record by the specified attribute and value.
+     *
+     * If no record is found an exception is thrown.
+     *
+     * @param string       $attribute
+     * @param string       $value
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return mixed
+     */
+    public function findByOrFail($attribute, $value, $columns = [])
+    {
+        return $this->whereEquals($attribute, $value)->firstOrFail($columns);
+    }
+
+    /**
+     * Finds a record using ambiguous name resolution.
+     *
+     * @param string|array $anr
+     * @param array|string $columns
+     *
+     * @return mixed
+     */
+    public function find($anr, $columns = [])
+    {
+        if (is_array($anr)) {
+            return $this->findMany($anr, $columns);
+        }
+
+        return $this->findBy($this->schema->anr(), $anr, $columns);
+    }
+
+    /**
+     * Finds multiple records using ambiguous name resolution.
+     *
+     * @param array $anrs
+     * @param array $columns
+     *
+     * @return mixed
+     */
+    public function findMany(array $anrs = [], $columns = [])
+    {
+        return $this->findManyBy($this->schema->anr(), $anrs, $columns);
+    }
+
+    /**
+     * Finds many records by the specified attribute.
+     *
+     * @param string $attribute
+     * @param array  $values
+     * @param array  $columns
+     *
+     * @return mixed
+     */
+    public function findManyBy($attribute, array $values = [], $columns = [])
+    {
+        $query = $this->select($columns);
+
+        foreach ($values as $value) {
+            $query->orWhere([$attribute => $value]);
+        }
+
+        return $query->get();
+    }
+
+    /**
+     * Finds a record using ambiguous name resolution.
+     * 
+     * If a record is not found, an exception is thrown.
+     *
+     * @param string       $anr
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return mixed
+     */
+    public function findOrFail($anr, $columns = [])
+    {
+        $entry = $this->find($anr, $columns);
+
+        // Make sure we check if the result is an entry or an array before
+        // we throw an exception in case the user wants raw results.
+        if (!$entry instanceof Model && !is_array($entry)) {
+            throw (new ModelNotFoundException())
+                ->setQuery($this->getUnescapedQuery(), $this->getDn());
+        }
+
+        return $entry;
+    }
+
+    /**
+     * Finds a record by its distinguished name.
+     *
+     * @param string|array $dn
+     * @param array|string $columns
+     *
+     * @return bool|Model
+     */
+    public function findByDn($dn, $columns = [])
+    {
+        return $this
+            ->setDn($dn)
+            ->read(true)
+            ->whereHas($this->schema->objectClass())
+            ->first($columns);
+    }
+
+    /**
+     * Finds a record by its distinguished name.
+     *
+     * Fails upon no records returned.
+     *
+     * @param string       $dn
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return Model
+     */
+    public function findByDnOrFail($dn, $columns = [])
+    {
+        return $this
+            ->setDn($dn)
+            ->read(true)
+            ->whereHas($this->schema->objectClass())
+            ->firstOrFail($columns);
+    }
+
+    /**
+     * Finds a record by its string GUID.
+     *
+     * @param string       $guid
+     * @param array|string $columns
+     *
+     * @return Model
+     */
+    public function findByGuid($guid, $columns = [])
+    {
+        if ($this->schema->objectGuidRequiresConversion()) {
+            $guid = Utilities::stringGuidToHex($guid);
+        }
+
+        return $this->select($columns)->whereRaw([
+            $this->schema->objectGuid() => $guid
+        ])->first();
+    }
+
+    /**
+     * Finds a record by its string GUID.
+     *
+     * Fails upon no records returned.
+     *
+     * @param string       $guid
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return mixed
+     */
+    public function findByGuidOrFail($guid, $columns = [])
+    {
+        if ($this->schema->objectGuidRequiresConversion()) {
+            $guid = Utilities::stringGuidToHex($guid);
+        }
+
+        return $this->select($columns)->whereRaw([
+            $this->schema->objectGuid() => $guid
+        ])->firstOrFail();
+    }
+
+    /**
+     * Finds a record by its Object SID.
+     *
+     * @param string       $sid
+     * @param array|string $columns
+     *
+     * @return mixed
+     */
+    public function findBySid($sid, $columns = [])
+    {
+        return $this->findBy($this->schema->objectSid(), $sid, $columns);
+    }
+
+    /**
+     * Finds a record by its Object SID.
+     *
+     * Fails upon no records returned.
+     *
+     * @param string       $sid
+     * @param array|string $columns
+     *
+     * @throws ModelNotFoundException
+     *
+     * @return mixed
+     */
+    public function findBySidOrFail($sid, $columns = [])
+    {
+        return $this->findByOrFail($this->schema->objectSid(), $sid, $columns);
+    }
+
+    /**
+     * Finds the Base DN of your domain controller.
+     *
+     * @return string|bool
+     */
+    public function findBaseDn()
+    {
+        $result = $this->setDn(null)
+            ->read()
+            ->raw()
+            ->whereHas($this->schema->objectClass())
+            ->first();
+
+        $key = $this->schema->defaultNamingContext();
+
+        if (is_array($result) && array_key_exists($key, $result)) {
+            if (array_key_exists(0, $result[$key])) {
+                return $result[$key][0];
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds the inserted fields to query on the current LDAP connection.
+     *
+     * @param array|string $columns
+     *
+     * @return Builder
+     */
+    public function select($columns = [])
+    {
+        $columns = is_array($columns) ? $columns : func_get_args();
+
+        if (!empty($columns)) {
+            $this->columns = $columns;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds a raw filter to the current query.
+     *
+     * @param array|string $filters
+     *
+     * @return Builder
+     */
+    public function rawFilter($filters = [])
+    {
+        $filters = is_array($filters) ? $filters : func_get_args();
+
+        foreach ($filters as $filter) {
+            $this->filters['raw'][] = $filter;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds a nested 'and' filter to the current query.
+     *
+     * @param Closure $closure
+     *
+     * @return Builder
+     */
+    public function andFilter(Closure $closure)
+    {
+        $query = $this->newNestedInstance($closure);
+
+        $filter = $this->grammar->compileAnd($query->getQuery());
+
+        return $this->rawFilter($filter);
+    }
+
+    /**
+     * Adds a nested 'or' filter to the current query.
+     *
+     * @param Closure $closure
+     *
+     * @return Builder
+     */
+    public function orFilter(Closure $closure)
+    {
+        $query = $this->newNestedInstance($closure);
+
+        $filter = $this->grammar->compileOr($query->getQuery());
+
+        return $this->rawFilter($filter);
+    }
+
+    /**
+     * Adds a nested 'not' filter to the current query.
+     *
+     * @param Closure $closure
+     *
+     * @return Builder
+     */
+    public function notFilter(Closure $closure)
+    {
+        $query = $this->newNestedInstance($closure);
+
+        $filter = $this->grammar->compileNot($query->getQuery());
+
+        return $this->rawFilter($filter);
+    }
+
+    /**
+     * Adds a where clause to the current query.
+     *
+     * @param string|array $field
+     * @param string       $operator
+     * @param string       $value
+     * @param string       $boolean
+     * @param bool         $raw
+     *
+     * @throws InvalidArgumentException
+     *
+     * @return Builder
+     */
+    public function where($field, $operator = null, $value = null, $boolean = 'and', $raw = false)
+    {
+        if (is_array($field)) {
+            // If the column is an array, we will assume it is an array of
+            // key-value pairs and can add them each as a where clause.
+            return $this->addArrayOfWheres($field, $boolean, $raw);
+        }
+
+        // We'll bypass the 'has' and 'notHas' operator since they
+        // only require two arguments inside the where method.
+        $bypass = [Operator::$has, Operator::$notHas];
+
+        // Here we will make some assumptions about the operator. If only
+        // 2 values are passed to the method, we will assume that
+        // the operator is an equals sign and keep going.
+        if (func_num_args() === 2 && in_array($operator, $bypass) === false) {
+            list($value, $operator) = [$operator, '='];
+        }
+
+        if (!in_array($operator, Operator::all())) {
+            throw new InvalidArgumentException("Invalid where operator: {$operator}");
+        }
+
+        // We'll escape the value if raw isn't requested.
+        $value = $raw ? $value : $this->escape($value);
+
+        $field = $this->escape($field, $ignore = null, 3);
+
+        $this->filters[$boolean][] = compact('field', 'operator', 'value');
+
+        return $this;
+    }
+
+    /**
+     * Adds a raw where clause to the current query.
+     *
+     * Values given to this method are not escaped.
+     *
+     * @param string|array $field
+     * @param string       $operator
+     * @param string       $value
+     *
+     * @return Builder
+     */
+    public function whereRaw($field, $operator = null, $value = null)
+    {
+        return $this->where($field, $operator, $value, 'and', true);
+    }
+
+    /**
+     * Adds a 'where equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereEquals($field, $value)
+    {
+        return $this->where($field, Operator::$equals, $value);
+    }
+
+    /**
+     * Adds a 'where not equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereNotEquals($field, $value)
+    {
+        return $this->where($field, Operator::$doesNotEqual, $value);
+    }
+
+    /**
+     * Adds a 'where approximately equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereApproximatelyEquals($field, $value)
+    {
+        return $this->where($field, Operator::$approximatelyEquals, $value);
+    }
+
+    /**
+     * Adds a 'where has' clause to the current query.
+     *
+     * @param string $field
+     *
+     * @return Builder
+     */
+    public function whereHas($field)
+    {
+        return $this->where($field, Operator::$has);
+    }
+
+    /**
+     * Adds a 'where not has' clause to the current query.
+     *
+     * @param string $field
+     *
+     * @return Builder
+     */
+    public function whereNotHas($field)
+    {
+        return $this->where($field, Operator::$notHas);
+    }
+
+    /**
+     * Adds a 'where contains' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereContains($field, $value)
+    {
+        return $this->where($field, Operator::$contains, $value);
+    }
+
+    /**
+     * Adds a 'where contains' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereNotContains($field, $value)
+    {
+        return $this->where($field, Operator::$notContains, $value);
+    }
+
+    /**
+     * Adds a 'between' clause to the current query.
+     *
+     * @param string $field
+     * @param array  $values
+     *
+     * @return Builder
+     */
+    public function whereBetween($field, array $values)
+    {
+        return $this->where([
+            [$field, '>=', $values[0]],
+            [$field, '<=', $values[1]],
+        ]);
+    }
+
+    /**
+     * Adds a 'where starts with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereStartsWith($field, $value)
+    {
+        return $this->where($field, Operator::$startsWith, $value);
+    }
+
+    /**
+     * Adds a 'where *not* starts with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereNotStartsWith($field, $value)
+    {
+        return $this->where($field, Operator::$notStartsWith, $value);
+    }
+
+    /**
+     * Adds a 'where ends with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereEndsWith($field, $value)
+    {
+        return $this->where($field, Operator::$endsWith, $value);
+    }
+
+    /**
+     * Adds a 'where *not* ends with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function whereNotEndsWith($field, $value)
+    {
+        return $this->where($field, Operator::$notEndsWith, $value);
+    }
+
+    /**
+     * Adds a enabled filter to the current query.
+     *
+     * @return Builder
+     */
+    public function whereEnabled()
+    {
+        return $this->rawFilter($this->schema->filterEnabled());
+    }
+
+    /**
+     * Adds a disabled filter to the current query.
+     *
+     * @return Builder
+     */
+    public function whereDisabled()
+    {
+        return $this->rawFilter($this->schema->filterDisabled());
+    }
+
+    /**
+     * Adds a 'member of' filter to the current query.
+     *
+     * @param string $dn
+     *
+     * @return Builder
+     */
+    public function whereMemberOf($dn)
+    {
+        return $this->whereEquals($this->schema->memberOfRecursive(), $dn);
+    }
+
+    /**
+     * Adds an 'or where' clause to the current query.
+     *
+     * @param string      $field
+     * @param string|null $operator
+     * @param string|null $value
+     *
+     * @return Builder
+     */
+    public function orWhere($field, $operator = null, $value = null)
+    {
+        return $this->where($field, $operator, $value, 'or');
+    }
+
+    /**
+     * Adds a raw or where clause to the current query.
+     *
+     * Values given to this method are not escaped.
+     *
+     * @param string $field
+     * @param string $operator
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereRaw($field, $operator = null, $value = null)
+    {
+        return $this->where($field, $operator, $value, 'or', true);
+    }
+
+    /**
+     * Adds an 'or where has' clause to the current query.
+     *
+     * @param string $field
+     *
+     * @return Builder
+     */
+    public function orWhereHas($field)
+    {
+        return $this->orWhere($field, Operator::$has);
+    }
+
+    /**
+     * Adds a 'where not has' clause to the current query.
+     *
+     * @param string $field
+     *
+     * @return Builder
+     */
+    public function orWhereNotHas($field)
+    {
+        return $this->orWhere($field, Operator::$notHas);
+    }
+
+    /**
+     * Adds an 'or where equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereEquals($field, $value)
+    {
+        return $this->orWhere($field, Operator::$equals, $value);
+    }
+
+    /**
+     * Adds an 'or where not equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereNotEquals($field, $value)
+    {
+        return $this->orWhere($field, Operator::$doesNotEqual, $value);
+    }
+
+    /**
+     * Adds a 'or where approximately equals' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereApproximatelyEquals($field, $value)
+    {
+        return $this->orWhere($field, Operator::$approximatelyEquals, $value);
+    }
+
+    /**
+     * Adds an 'or where contains' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereContains($field, $value)
+    {
+        return $this->orWhere($field, Operator::$contains, $value);
+    }
+
+    /**
+     * Adds an 'or where *not* contains' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereNotContains($field, $value)
+    {
+        return $this->orWhere($field, Operator::$notContains, $value);
+    }
+
+    /**
+     * Adds an 'or where starts with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereStartsWith($field, $value)
+    {
+        return $this->orWhere($field, Operator::$startsWith, $value);
+    }
+
+    /**
+     * Adds an 'or where *not* starts with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereNotStartsWith($field, $value)
+    {
+        return $this->orWhere($field, Operator::$notStartsWith, $value);
+    }
+
+    /**
+     * Adds an 'or where ends with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereEndsWith($field, $value)
+    {
+        return $this->orWhere($field, Operator::$endsWith, $value);
+    }
+
+    /**
+     * Adds an 'or where *not* ends with' clause to the current query.
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return Builder
+     */
+    public function orWhereNotEndsWith($field, $value)
+    {
+        return $this->orWhere($field, Operator::$notEndsWith, $value);
+    }
+
+    /**
+     * Adds an 'or where member of' filter to the current query.
+     *
+     * @param string $dn
+     *
+     * @return Builder
+     */
+    public function orWhereMemberOf($dn)
+    {
+        return $this->orWhereEquals($this->schema->memberOfRecursive(), $dn);
+    }
+
+    /**
+     * Returns true / false depending if the current object
+     * contains selects.
+     *
+     * @return bool
+     */
+    public function hasSelects()
+    {
+        return count($this->getSelects()) > 0;
+    }
+
+    /**
+     * Returns the current selected fields to retrieve.
+     *
+     * @return array
+     */
+    public function getSelects()
+    {
+        $selects = $this->columns;
+
+        // If the asterisk is not provided in the selected columns, we need to
+        // ensure we always select the object class and category, as these
+        // are used for constructing models. The asterisk indicates that
+        // we want all attributes returned for LDAP records.
+        if (!in_array('*', $selects)) {
+            $selects[] = $this->schema->objectCategory();
+            $selects[] = $this->schema->objectClass();
+        }
+
+        return $selects;
+    }
+
+    /**
+     * Sorts the LDAP search results by the specified field and direction.
+     *
+     * @param string   $field
+     * @param string   $direction
+     * @param int|null $flags
+     *
+     * @return Builder
+     */
+    public function sortBy($field, $direction = 'asc', $flags = null)
+    {
+        $this->sortByField = $field;
+
+        // Normalize direction.
+        $direction = strtolower($direction);
+
+        if ($direction === 'asc' || $direction === 'desc') {
+            $this->sortByDirection = $direction;
+        }
+
+        if (is_null($flags)) {
+            $this->sortByFlags = SORT_NATURAL + SORT_FLAG_CASE;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Set the query to search on the base distinguished name.
+     *
+     * This will result in one record being returned.
+     *
+     * @return Builder
+     */
+    public function read()
+    {
+        $this->type = 'read';
+
+        return $this;
+    }
+
+    /**
+     * Set the query to search one level on the base distinguished name.
+     *
+     * @return Builder
+     */
+    public function listing()
+    {
+        $this->type = 'listing';
+
+        return $this;
+    }
+
+    /**
+     * Sets the query to search the entire directory on the base distinguished name.
+     *
+     * @return Builder
+     */
+    public function recursive()
+    {
+        $this->type = 'search';
+
+        return $this;
+    }
+
+    /**
+     * Sets the recursive property to tell the search whether or
+     * not to return the LDAP results in their raw format.
+     *
+     * @param bool $raw
+     *
+     * @return Builder
+     */
+    public function raw($raw = true)
+    {
+        $this->raw = (bool) $raw;
+
+        return $this;
+    }
+
+    /**
+     * Sets the nested property to tell the Grammar instance whether
+     * or not the current query is already nested.
+     *
+     * @param bool $nested
+     *
+     * @return Builder
+     */
+    public function nested($nested = true)
+    {
+        $this->nested = (bool) $nested;
+
+        return $this;
+    }
+
+    /**
+     * Returns true / false if the current query is nested.
+     *
+     * @return bool
+     */
+    public function isNested()
+    {
+        return $this->nested === true;
+    }
+
+    /**
+     * Returns an escaped string for use in an LDAP filter.
+     *
+     * @param string $value
+     * @param string $ignore
+     * @param int    $flags
+     *
+     * @return string
+     */
+    public function escape($value, $ignore = '', $flags = 0)
+    {
+        return Utilities::escape($value, $ignore, $flags);
+    }
+
+    /**
+     * Returns the query builders sort by field.
+     *
+     * @return string
+     */
+    public function getSortByField()
+    {
+        return $this->sortByField;
+    }
+
+    /**
+     * Returns the query builders sort by direction.
+     *
+     * @return string
+     */
+    public function getSortByDirection()
+    {
+        return $this->sortByDirection;
+    }
+
+    /**
+     * Returns the query builders sort by flags.
+     *
+     * @return int
+     */
+    public function getSortByFlags()
+    {
+        return $this->sortByFlags;
+    }
+
+    /**
+     * Returns bool that determines whether the current
+     * query builder will return raw results.
+     *
+     * @return bool
+     */
+    public function isRaw()
+    {
+        return $this->raw;
+    }
+
+    /**
+     * Returns bool that determines whether the current
+     * query builder will return paginated results.
+     *
+     * @return bool
+     */
+    public function isPaginated()
+    {
+        return $this->paginated;
+    }
+
+    /**
+     * Returns bool that determines whether the current
+     * query builder will return sorted results.
+     *
+     * @return bool
+     */
+    public function isSorted()
+    {
+        return $this->sortByField ? true : false;
+    }
+
+    /**
+     * Handle dynamic method calls on the query builder
+     * object to be directed to the query processor.
+     *
+     * @param string $method
+     * @param array  $parameters
+     *
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        if (Str::startsWith($method, 'where')) {
+            return $this->dynamicWhere($method, $parameters);
+        }
+
+        return call_user_func_array([$this->newProcessor(), $method], $parameters);
+    }
+
+    /**
+     * Handles dynamic "where" clauses to the query.
+     *
+     * @param string $method
+     * @param string $parameters
+     *
+     * @return Builder
+     */
+    public function dynamicWhere($method, $parameters)
+    {
+        $finder = substr($method, 5);
+
+        $segments = preg_split('/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+        // The connector variable will determine which connector will be used for the
+        // query condition. We will change it as we come across new boolean values
+        // in the dynamic method strings, which could contain a number of these.
+        $connector = 'and';
+
+        $index = 0;
+
+        foreach ($segments as $segment) {
+            // If the segment is not a boolean connector, we can assume it is a column's name
+            // and we will add it to the query as a new constraint as a where clause, then
+            // we can keep iterating through the dynamic method string's segments again.
+            if ($segment != 'And' && $segment != 'Or') {
+                $this->addDynamic($segment, $connector, $parameters, $index);
+
+                $index++;
+            }
+
+            // Otherwise, we will store the connector so we know how the next where clause we
+            // find in the query should be connected to the previous ones, meaning we will
+            // have the proper boolean connector to connect the next where clause found.
+            else {
+                $connector = $segment;
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Adds an array of wheres to the current query.
+     *
+     * @param array  $wheres
+     * @param string $boolean
+     * @param bool   $raw
+     *
+     * @return Builder
+     */
+    protected function addArrayOfWheres($wheres, $boolean, $raw)
+    {
+        foreach ($wheres as $key => $value) {
+            if (is_numeric($key) && is_array($value)) {
+                // If the key is numeric and the value is an array, we'll
+                // assume we've been given an array with conditionals.
+                $this->where($value[0], $value[1], $value[2]);
+            } else {
+                // Otherwise, we'll assume the array is an equals clause.
+                $this->where($key, Operator::$equals, $value, $boolean, $raw);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Add a single dynamic where clause statement to the query.
+     *
+     * @param string $segment
+     * @param string $connector
+     * @param array  $parameters
+     * @param int    $index
+     *
+     * @return void
+     */
+    protected function addDynamic($segment, $connector, $parameters, $index)
+    {
+        // Once we have parsed out the columns and formatted the boolean operators we
+        // are ready to add it to this query as a where clause just like any other
+        // clause on the query. Then we'll increment the parameter index values.
+        $bool = strtolower($connector);
+
+        $this->where(Str::snake($segment), '=', $parameters[$index], $bool);
+    }
+
+    /**
+     * Returns a new query Processor instance.
+     *
+     * @return Processor
+     */
+    protected function newProcessor()
+    {
+        return new Processor($this);
+    }
+}

+ 267 - 0
api/vendor/adldap2/adldap2/src/Query/Factory.php

@@ -0,0 +1,267 @@
+<?php
+
+namespace Adldap\Query;
+
+use Adldap\Models\RootDse;
+use Adldap\Schemas\ActiveDirectory;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Connections\ConnectionInterface;
+
+/**
+ * Adldap2 Search Factory.
+ *
+ * @package Adldap\Search
+ *
+ * @mixin Builder
+ */
+class Factory
+{
+    /**
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * Stores the current query builder instance.
+     *
+     * @var Builder
+     */
+    protected $query;
+
+    /**
+     * Stores the current schema instance.
+     *
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * Constructor.
+     *
+     * @param ConnectionInterface  $connection
+     * @param SchemaInterface|null $schema
+     * @param string               $baseDn
+     */
+    public function __construct(ConnectionInterface $connection, SchemaInterface $schema = null, $baseDn = '')
+    {
+        $this->setConnection($connection)
+            ->setSchema($schema)
+            ->setQuery($this->newQuery($baseDn));
+    }
+
+    /**
+     * Sets the connection property.
+     *
+     * @param ConnectionInterface $connection
+     *
+     * @return $this
+     */
+    public function setConnection(ConnectionInterface $connection)
+    {
+        $this->connection = $connection;
+
+        return $this;
+    }
+
+    /**
+     * Sets the query property.
+     *
+     * @param Builder $query
+     *
+     * @return $this
+     */
+    public function setQuery(Builder $query)
+    {
+        $this->query = $query;
+
+        return $this;
+    }
+
+    /**
+     * Sets the schema property.
+     *
+     * @param SchemaInterface|null $schema
+     *
+     * @return $this
+     */
+    public function setSchema(SchemaInterface $schema = null)
+    {
+        $this->schema = $schema ?: new ActiveDirectory();
+
+        return $this;
+    }
+
+    /**
+     * Returns a new query builder instance.
+     *
+     * @param string $baseDn
+     *
+     * @return Builder
+     */
+    public function newQuery($baseDn = '')
+    {
+        return (new Builder($this->connection, $this->newGrammar(), $this->schema))
+            ->in($baseDn);
+    }
+
+    /**
+     * Returns the current query Builder instance.
+     *
+     * @return Builder
+     */
+    public function getQuery()
+    {
+        return $this->query;
+    }
+
+    /**
+     * Returns a new query grammar instance.
+     *
+     * @return Grammar
+     */
+    public function newGrammar()
+    {
+        return new Grammar();
+    }
+
+    /**
+     * Performs a global 'all' search query on the current
+     * connection by performing a search for all entries
+     * that contain a common name attribute.
+     *
+     * @return \Illuminate\Support\Collection|array
+     */
+    public function all()
+    {
+        return $this->query->whereHas($this->schema->commonName())->get();
+    }
+
+    /**
+     * Alias for the `all()` method.
+     *
+     * @return \Illuminate\Support\Collection|array
+     */
+    public function get()
+    {
+        return $this->all();
+    }
+
+    /**
+     * Returns a query builder limited to users.
+     *
+     * @return Builder
+     */
+    public function users()
+    {
+        return $this->where([
+            $this->schema->objectClass()    => $this->schema->objectClassPerson(),
+            $this->schema->objectCategory() => $this->schema->objectCategoryPerson(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to printers.
+     *
+     * @return Builder
+     */
+    public function printers()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassPrinter(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to organizational units.
+     *
+     * @return Builder
+     */
+    public function ous()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassOu(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to groups.
+     *
+     * @return Builder
+     */
+    public function groups()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassGroup(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to exchange servers.
+     *
+     * @return Builder
+     */
+    public function containers()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassContainer(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to exchange servers.
+     *
+     * @return Builder
+     */
+    public function contacts()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassContact(),
+        ]);
+    }
+
+    /**
+     * Returns a query builder limited to exchange servers.
+     *
+     * @return Builder
+     */
+    public function computers()
+    {
+        return $this->where([
+            $this->schema->objectClass() => $this->schema->objectClassComputer(),
+        ]);
+    }
+
+    /**
+     * Returns the root DSE record.
+     *
+     * @return RootDse|null
+     */
+    public function getRootDse()
+    {
+        $root = $this->query->newInstance()
+            ->in('')
+            ->read(true)
+            ->whereHas($this->schema->objectClass())
+            ->first();
+
+        if ($root) {
+            return (new RootDse([], $this->query))
+                ->setRawAttributes($root->getAttributes());
+        }
+    }
+
+    /**
+     * Handle dynamic method calls on the query builder object.
+     *
+     * @param string $method
+     * @param array  $parameters
+     *
+     * @return mixed
+     */
+    public function __call($method, $parameters)
+    {
+        $query = $this->query->newInstance();
+
+        return call_user_func_array([$query, $method], $parameters);
+    }
+}

+ 390 - 0
api/vendor/adldap2/adldap2/src/Query/Grammar.php

@@ -0,0 +1,390 @@
+<?php
+
+namespace Adldap\Query;
+
+class Grammar
+{
+    /**
+     * Wraps a query string in brackets.
+     *
+     * Produces: (query)
+     *
+     * @param string $query
+     * @param string $prefix
+     * @param string $suffix
+     *
+     * @return string
+     */
+    public function wrap($query, $prefix = '(', $suffix = ')')
+    {
+        return $prefix.$query.$suffix;
+    }
+
+    /**
+     * Compiles the Builder instance into an LDAP query string.
+     *
+     * @param Builder $builder
+     *
+     * @return string
+     */
+    public function compile(Builder $builder)
+    {
+        $ands = $builder->filters['and'];
+        $ors = $builder->filters['or'];
+        $raws = $builder->filters['raw'];
+
+        $query = $this->concatenate($raws);
+
+        $query = $this->compileWheres($ands, $query);
+
+        $query = $this->compileOrWheres($ors, $query);
+
+        // We need to check if the query is already nested, otherwise
+        // we'll nest it here and return the result.
+        if (!$builder->isNested()) {
+            $total = count($ands) + count($raws);
+
+            // Make sure we wrap the query in an 'and' if using
+            // multiple filters. We also need to check if only
+            // one where is used with multiple orWheres, that
+            // we wrap it in an `and` query.
+            if ($total > 1 || (count($ands) === 1 && count($ors) > 0)) {
+                $query = $this->compileAnd($query);
+            }
+        }
+
+        return $query;
+    }
+
+    /**
+     * Concatenates filters into a single string.
+     *
+     * @param array $bindings
+     *
+     * @return string
+     */
+    public function concatenate(array $bindings = [])
+    {
+        // Filter out empty query segments.
+        $bindings = array_filter($bindings, function ($value) {
+            return (string) $value !== '';
+        });
+
+        return implode('', $bindings);
+    }
+
+    /**
+     * Returns a query string for equals.
+     *
+     * Produces: (field=value)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileEquals($field, $value)
+    {
+        return $this->wrap($field.Operator::$equals.$value);
+    }
+
+    /**
+     * Returns a query string for does not equal.
+     *
+     * Produces: (!(field=value))
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileDoesNotEqual($field, $value)
+    {
+        return $this->compileNot($this->compileEquals($field, $value));
+    }
+
+    /**
+     * Alias for does not equal operator (!=) operator.
+     *
+     * Produces: (!(field=value))
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileDoesNotEqualAlias($field, $value)
+    {
+        return $this->compileDoesNotEqual($field, $value);
+    }
+
+    /**
+     * Returns a query string for greater than or equals.
+     *
+     * Produces: (field>=value)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileGreaterThanOrEquals($field, $value)
+    {
+        return $this->wrap($field.Operator::$greaterThanOrEquals.$value);
+    }
+
+    /**
+     * Returns a query string for less than or equals.
+     *
+     * Produces: (field<=value)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileLessThanOrEquals($field, $value)
+    {
+        return $this->wrap($field.Operator::$lessThanOrEquals.$value);
+    }
+
+    /**
+     * Returns a query string for approximately equals.
+     *
+     * Produces: (field~=value)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileApproximatelyEquals($field, $value)
+    {
+        return $this->wrap($field.Operator::$approximatelyEquals.$value);
+    }
+
+    /**
+     * Returns a query string for starts with.
+     *
+     * Produces: (field=value*)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileStartsWith($field, $value)
+    {
+        return $this->wrap($field.Operator::$equals.$value.Operator::$has);
+    }
+
+    /**
+     * Returns a query string for does not start with.
+     *
+     * Produces: (!(field=*value))
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileNotStartsWith($field, $value)
+    {
+        return $this->compileNot($this->compileStartsWith($field, $value));
+    }
+
+    /**
+     * Returns a query string for ends with.
+     *
+     * Produces: (field=*value)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileEndsWith($field, $value)
+    {
+        return $this->wrap($field.Operator::$equals.Operator::$has.$value);
+    }
+
+    /**
+     * Returns a query string for does not end with.
+     *
+     * Produces: (!(field=value*))
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileNotEndsWith($field, $value)
+    {
+        return $this->compileNot($this->compileEndsWith($field, $value));
+    }
+
+    /**
+     * Returns a query string for contains.
+     *
+     * Produces: (field=*value*)
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileContains($field, $value)
+    {
+        return $this->wrap($field.Operator::$equals.Operator::$has.$value.Operator::$has);
+    }
+
+    /**
+     * Returns a query string for does not contain.
+     *
+     * Produces: (!(field=*value*))
+     *
+     * @param string $field
+     * @param string $value
+     *
+     * @return string
+     */
+    public function compileNotContains($field, $value)
+    {
+        return $this->compileNot($this->compileContains($field, $value));
+    }
+
+    /**
+     * Returns a query string for a where has.
+     *
+     * Produces: (field=*)
+     *
+     * @param string $field
+     *
+     * @return string
+     */
+    public function compileHas($field)
+    {
+        return $this->wrap($field.Operator::$equals.Operator::$has);
+    }
+
+    /**
+     * Returns a query string for a where does not have.
+     *
+     * Produces: (!(field=*))
+     *
+     * @param string $field
+     *
+     * @return string
+     */
+    public function compileNotHas($field)
+    {
+        return $this->compileNot($this->compileHas($field));
+    }
+
+    /**
+     * Wraps the inserted query inside an AND operator.
+     *
+     * Produces: (&query)
+     *
+     * @param string $query
+     *
+     * @return string
+     */
+    public function compileAnd($query)
+    {
+        return $query ? $this->wrap($query, '(&') : '';
+    }
+
+    /**
+     * Wraps the inserted query inside an OR operator.
+     *
+     * Produces: (|query)
+     *
+     * @param string $query
+     *
+     * @return string
+     */
+    public function compileOr($query)
+    {
+        return $query ? $this->wrap($query, '(|') : '';
+    }
+
+    /**
+     * Wraps the inserted query inside an NOT operator.
+     *
+     * @param string $query
+     *
+     * @return string
+     */
+    public function compileNot($query)
+    {
+        return $query ? $this->wrap($query, '(!') : '';
+    }
+
+    /**
+     * Assembles all where clauses in the current wheres property.
+     *
+     * @param array  $wheres
+     * @param string $query
+     *
+     * @return string
+     */
+    protected function compileWheres(array $wheres = [], $query = '')
+    {
+        foreach ($wheres as $where) {
+            $query .= $this->compileWhere($where);
+        }
+
+        return $query;
+    }
+
+    /**
+     * Assembles all or where clauses in the current orWheres property.
+     *
+     * @param array  $orWheres
+     * @param string $query
+     *
+     * @return string
+     */
+    protected function compileOrWheres(array $orWheres = [], $query = '')
+    {
+        $or = '';
+
+        foreach ($orWheres as $where) {
+            $or .= $this->compileWhere($where);
+        }
+
+        // Make sure we wrap the query in an 'or' if using multiple
+        // orWheres. For example (|(QUERY)(ORWHEREQUERY)).
+        if (($query && count($orWheres) > 0) || count($orWheres) > 1) {
+            $query .= $this->compileOr($or);
+        } else {
+            $query .= $or;
+        }
+
+        return $query;
+    }
+
+    /**
+     * Assembles a single where query based
+     * on its operator and returns it.
+     *
+     * @param array $where
+     *
+     * @return string|null
+     */
+    protected function compileWhere(array $where)
+    {
+        // Get the name of the operator.
+        if ($name = array_search($where['operator'], Operator::all())) {
+            // If the name was found we'll camel case it
+            // to run it through the compile method.
+            $method = 'compile'.ucfirst($name);
+
+            // Make sure the compile method exists for the operator.
+            if (method_exists($this, $method)) {
+                return $this->{$method}($where['field'], $where['value']);
+            }
+        }
+    }
+}

+ 117 - 0
api/vendor/adldap2/adldap2/src/Query/Operator.php

@@ -0,0 +1,117 @@
+<?php
+
+namespace Adldap\Query;
+
+use ReflectionClass;
+
+class Operator
+{
+    /**
+     * The 'has' wildcard operator.
+     *
+     * @var string
+     */
+    public static $has = '*';
+
+    /**
+     * The custom `notHas` operator.
+     *
+     * @var string
+     */
+    public static $notHas = '!*';
+
+    /**
+     * The equals operator.
+     *
+     * @var string
+     */
+    public static $equals = '=';
+
+    /**
+     * The does not equal operator.
+     *
+     * @var string
+     */
+    public static $doesNotEqual = '!';
+
+    /**
+     * The does not equal operator (alias)
+     *
+     * @var string
+     */
+    public static $doesNotEqualAlias = '!=';
+
+    /**
+     * The greater than or equal to operator.
+     *
+     * @var string
+     */
+    public static $greaterThanOrEquals = '>=';
+
+    /**
+     * The less than or equal to operator.
+     *
+     * @var string
+     */
+    public static $lessThanOrEquals = '<=';
+
+    /**
+     * The approximately equal to operator.
+     *
+     * @var string
+     */
+    public static $approximatelyEquals = '~=';
+
+    /**
+     * The custom starts with operator.
+     *
+     * @var string
+     */
+    public static $startsWith = 'starts_with';
+
+    /**
+     * The custom not starts with operator.
+     *
+     * @var string
+     */
+    public static $notStartsWith = 'not_starts_with';
+
+    /**
+     * The custom ends with operator.
+     *
+     * @var string
+     */
+    public static $endsWith = 'ends_with';
+
+    /**
+     * The custom not ends with operator.
+     *
+     * @var string
+     */
+    public static $notEndsWith = 'not_ends_with';
+
+    /**
+     * The custom contains operator.
+     *
+     * @var string
+     */
+    public static $contains = 'contains';
+
+    /**
+     * The custom not contains operator.
+     *
+     * @var string
+     */
+    public static $notContains = 'not_contains';
+
+    /**
+     * Returns all available operators.
+     *
+     * @return array
+     */
+    public static function all()
+    {
+        return (new ReflectionClass(new static()))
+            ->getStaticProperties();
+    }
+}

+ 206 - 0
api/vendor/adldap2/adldap2/src/Query/Paginator.php

@@ -0,0 +1,206 @@
+<?php
+
+namespace Adldap\Query;
+
+use Countable;
+use ArrayIterator;
+use IteratorAggregate;
+
+class Paginator implements Countable, IteratorAggregate
+{
+    /**
+     * The complete results array.
+     *
+     * @var array
+     */
+    protected $results = [];
+
+    /**
+     * The total amount of pages.
+     *
+     * @var int
+     */
+    protected $pages;
+
+    /**
+     * The amount of entries per page.
+     *
+     * @var int
+     */
+    protected $perPage;
+
+    /**
+     * The current page number.
+     *
+     * @var int
+     */
+    protected $currentPage;
+
+    /**
+     * The current entry offset number.
+     *
+     * @var int
+     */
+    protected $currentOffset;
+
+    /**
+     * Constructor.
+     *
+     * @param array $results
+     * @param int   $perPage
+     * @param int   $currentPage
+     * @param int   $pages
+     */
+    public function __construct(array $results = [], $perPage = 50, $currentPage = 0, $pages = 0)
+    {
+        $this->setResults($results)
+            ->setPerPage($perPage)
+            ->setCurrentPage($currentPage)
+            ->setPages($pages)
+            ->setCurrentOffset(($this->getCurrentPage() * $this->getPerPage()));
+    }
+
+    /**
+     * Get an iterator for the entries.
+     *
+     * @return ArrayIterator
+     */
+    public function getIterator()
+    {
+        $entries = array_slice($this->getResults(), $this->getCurrentOffset(), $this->getPerPage(), true);
+
+        return new ArrayIterator($entries);
+    }
+
+    /**
+     * Returns the complete results array.
+     *
+     * @return array
+     */
+    public function getResults()
+    {
+        return $this->results;
+    }
+
+    /**
+     * Returns the total amount of pages
+     * in a paginated result.
+     *
+     * @return int
+     */
+    public function getPages()
+    {
+        return $this->pages;
+    }
+
+    /**
+     * Returns the total amount of entries
+     * allowed per page.
+     *
+     * @return int
+     */
+    public function getPerPage()
+    {
+        return $this->perPage;
+    }
+
+    /**
+     * Returns the current page number.
+     *
+     * @return int
+     */
+    public function getCurrentPage()
+    {
+        return $this->currentPage;
+    }
+
+    /**
+     * Returns the current offset number.
+     *
+     * @return int
+     */
+    public function getCurrentOffset()
+    {
+        return $this->currentOffset;
+    }
+
+    /**
+     * Returns the total amount of results.
+     *
+     * @return int
+     */
+    public function count()
+    {
+        return count($this->results);
+    }
+
+    /**
+     * Sets the results array property.
+     *
+     * @param array $results
+     *
+     * @return Paginator
+     */
+    protected function setResults(array $results)
+    {
+        $this->results = $results;
+
+        return $this;
+    }
+
+    /**
+     * Sets the total number of pages.
+     *
+     * @param int $pages
+     *
+     * @return Paginator
+     */
+    protected function setPages($pages = 0)
+    {
+        $this->pages = (int) $pages;
+
+        return $this;
+    }
+
+    /**
+     * Sets the number of entries per page.
+     *
+     * @param int $perPage
+     *
+     * @return Paginator
+     */
+    protected function setPerPage($perPage = 50)
+    {
+        $this->perPage = (int) $perPage;
+
+        return $this;
+    }
+
+    /**
+     * Sets the current page number.
+     *
+     * @param int $currentPage
+     *
+     * @return Paginator
+     */
+    protected function setCurrentPage($currentPage = 0)
+    {
+        $this->currentPage = (int) $currentPage;
+
+        return $this;
+    }
+
+    /**
+     * Sets the current offset number.
+     *
+     * @param int $offset
+     *
+     * @return Paginator
+     */
+    protected function setCurrentOffset($offset = 0)
+    {
+        $this->currentOffset = (int) $offset;
+
+        return $this;
+    }
+}

+ 222 - 0
api/vendor/adldap2/adldap2/src/Query/Processor.php

@@ -0,0 +1,222 @@
+<?php
+
+namespace Adldap\Query;
+
+use Illuminate\Support\Arr;
+use Illuminate\Support\Collection;
+use Adldap\Models\Entry;
+use Adldap\Models\Model;
+use Adldap\Schemas\SchemaInterface;
+use Adldap\Connections\ConnectionInterface;
+
+class Processor
+{
+    /**
+     * @var Builder
+     */
+    protected $builder;
+
+    /**
+     * @var ConnectionInterface
+     */
+    protected $connection;
+
+    /**
+     * @var SchemaInterface
+     */
+    protected $schema;
+
+    /**
+     * Constructor.
+     *
+     * @param Builder $builder
+     */
+    public function __construct(Builder $builder)
+    {
+        $this->builder = $builder;
+        $this->connection = $builder->getConnection();
+        $this->schema = $builder->getSchema();
+    }
+
+    /**
+     * Processes LDAP search results and constructs their model instances.
+     *
+     * @param resource $results
+     *
+     * @return array
+     */
+    public function process($results)
+    {
+        // Normalize entries. Get entries returns false on failure.
+        // We'll always want an array in this situation.
+        $entries = $this->connection->getEntries($results) ?: [];
+
+        if ($this->builder->isRaw()) {
+            // If the builder is asking for a raw
+            // LDAP result, we can return here.
+            return $entries;
+        }
+
+        $models = [];
+
+        if (Arr::has($entries, 'count')) {
+            for ($i = 0; $i < $entries['count']; $i++) {
+                // We'll go through each entry and construct a new
+                // model instance with the raw LDAP attributes.
+                $models[] = $this->newLdapEntry($entries[$i]);
+            }
+        }
+
+        if (!$this->builder->isPaginated()) {
+            // If the current query isn't paginated,
+            // we'll sort the models array here.
+            $models = $this->processSort($models);
+        }
+
+        return $models;
+    }
+
+    /**
+     * Processes paginated LDAP results.
+     *
+     * @param array $pages
+     * @param int   $perPage
+     * @param int   $currentPage
+     *
+     * @return Paginator
+     */
+    public function processPaginated(array $pages = [], $perPage = 50, $currentPage = 0)
+    {
+        $models = [];
+
+        foreach ($pages as $results) {
+            // Go through each page and process the results into an objects array.
+            $models = array_merge($models, $this->process($results));
+        }
+
+        $models = $this->processSort($models)->toArray();
+
+        return $this->newPaginator($models, $perPage, $currentPage, count($pages));
+    }
+
+    /**
+     * Returns a new LDAP Entry instance.
+     *
+     * @param array $attributes
+     *
+     * @return Entry
+     */
+    public function newLdapEntry(array $attributes = [])
+    {
+        $objectClass = $this->schema->objectClass();
+
+        if (array_key_exists($objectClass, $attributes) && array_key_exists(0, $attributes[$objectClass])) {
+            // Retrieve all of the object classes from the LDAP
+            // entry and lowercase them for comparisons.
+            $classes = array_map('strtolower', $attributes[$objectClass]);
+
+            // Retrieve the model mapping.
+            $models = $this->map();
+
+            // Retrieve the object class mappings (with strtolower keys).
+            $mappings = array_map('strtolower', array_keys($models));
+
+            // Retrieve the model from the map using the entry's object class.
+            $map = array_intersect($mappings, $classes);
+
+            if (count($map) > 0) {
+                // Retrieve the model using the object class.
+                $model = $models[current($map)];
+
+                // Construct and return a new model.
+                return $this->newModel([], $model)
+                    ->setRawAttributes($attributes);
+            }
+        }
+
+        // A default entry model if the object class isn't found.
+        return $this->newModel()->setRawAttributes($attributes);
+    }
+
+    /**
+     * Creates a new model instance.
+     *
+     * @param array       $attributes
+     * @param string|null $model
+     *
+     * @return mixed|Entry
+     */
+    public function newModel($attributes = [], $model = null)
+    {
+        $model = (class_exists($model) ? $model : Entry::class);
+
+        return new $model($attributes, $this->builder);
+    }
+
+    /**
+     * Returns a new Paginator object instance.
+     *
+     * @param array $models
+     * @param int   $perPage
+     * @param int   $currentPage
+     * @param int   $pages
+     *
+     * @return Paginator
+     */
+    public function newPaginator(array $models = [], $perPage = 25, $currentPage = 0, $pages = 1)
+    {
+        return new Paginator($models, $perPage, $currentPage, $pages);
+    }
+
+    /**
+     * Returns a new doctrine array collection instance.
+     *
+     * @param array $items
+     *
+     * @return Collection
+     */
+    public function newCollection(array $items = [])
+    {
+        return new Collection($items);
+    }
+
+    /**
+     * Returns the object class model class mapping.
+     *
+     * @return array
+     */
+    public function map()
+    {
+        return [
+            $this->schema->objectClassComputer()    => $this->schema->computerModel(),
+            $this->schema->objectClassContact()     => $this->schema->contactModel(),
+            $this->schema->objectClassPerson()      => $this->schema->userModel(),
+            $this->schema->objectClassGroup()       => $this->schema->groupModel(),
+            $this->schema->objectClassContainer()   => $this->schema->containerModel(),
+            $this->schema->objectClassPrinter()     => $this->schema->printerModel(),
+            $this->schema->objectClassOu()          => $this->schema->organizationalUnitModel(),
+        ];
+    }
+
+    /**
+     * Sorts LDAP search results.
+     *
+     * @param array $models
+     *
+     * @return Collection
+     */
+    protected function processSort(array $models = [])
+    {
+        $field = $this->builder->getSortByField();
+
+        $flags = $this->builder->getSortByFlags();
+
+        $direction = $this->builder->getSortByDirection();
+
+        $desc = ($direction === 'desc' ? true : false);
+
+        return $this->newCollection($models)->sortBy(function (Model $model) use ($field) {
+            return $model->getFirstAttribute($field);
+        }, $flags, $desc);
+    }
+}

+ 1223 - 0
api/vendor/adldap2/adldap2/src/Schemas/ActiveDirectory.php

@@ -0,0 +1,1223 @@
+<?php
+
+namespace Adldap\Schemas;
+
+use Adldap\Models\User;
+use Adldap\Models\Entry;
+use Adldap\Models\Group;
+use Adldap\Models\Contact;
+use Adldap\Models\Printer;
+use Adldap\Models\Computer;
+use Adldap\Models\Container;
+use Adldap\Models\OrganizationalUnit;
+
+class ActiveDirectory implements SchemaInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function accountExpires()
+    {
+        return 'accountexpires';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function accountName()
+    {
+        return 'samaccountname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function accountType()
+    {
+        return 'samaccounttype';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function adminDisplayName()
+    {
+        return 'admindisplayname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function anr()
+    {
+        return 'anr';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function badPasswordCount()
+    {
+        return 'badpwdcount';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function badPasswordTime()
+    {
+        return 'badpasswordtime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function commonName()
+    {
+        return 'cn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function company()
+    {
+        return 'company';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function computer()
+    {
+        return 'computer';
+    }
+
+    /**
+     * The class name of the Computer model.
+     *
+     * @return string
+     */
+    public function computerModel()
+    {
+        return Computer::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function configurationNamingContext()
+    {
+        return 'configurationnamingcontext';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function contact()
+    {
+        return 'contact';
+    }
+
+    /**
+     * The class name of the Contact model.
+     *
+     * @return string
+     */
+    public function contactModel()
+    {
+        return Contact::class;
+    }
+
+    /**
+     * The class name of the Container model.
+     *
+     * @return string
+     */
+    public function containerModel()
+    {
+        return Container::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function country()
+    {
+        return 'c';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createdAt()
+    {
+        return 'whencreated';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function currentTime()
+    {
+        return 'currenttime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function defaultNamingContext()
+    {
+        return 'defaultnamingcontext';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function department()
+    {
+        return 'department';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function departmentNumber()
+    {
+        return 'departmentnumber';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function description()
+    {
+        return 'description';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function displayName()
+    {
+        return 'displayname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function distinguishedName()
+    {
+        return 'distinguishedname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function distinguishedNameSubKey()
+    {
+        return 0;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function dnsHostName()
+    {
+        return 'dnshostname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function domainComponent()
+    {
+        return 'dc';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function driverName()
+    {
+        return 'drivername';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function driverVersion()
+    {
+        return 'driverversion';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function email()
+    {
+        return 'mail';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function emailNickname()
+    {
+        return 'mailnickname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function employeeId()
+    {
+        return 'employeeid';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function employeeNumber()
+    {
+        return 'employeenumber';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function employeeType()
+    {
+        return 'employeetype';
+    }
+
+    /**
+     * The class name of the Entry model.
+     *
+     * @return string
+     */
+    public function entryModel()
+    {
+        return Entry::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function false()
+    {
+        return 'FALSE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function filterEnabled()
+    {
+        return '(!(UserAccountControl:1.2.840.113556.1.4.803:=2))';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function filterDisabled()
+    {
+        return '(UserAccountControl:1.2.840.113556.1.4.803:=2)';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function firstName()
+    {
+        return 'givenname';
+    }
+
+    /**
+     * The class name of the Group model.
+     *
+     * @return string
+     */
+    public function groupModel()
+    {
+        return Group::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function groupType()
+    {
+        return 'grouptype';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function homeAddress()
+    {
+        return 'homepostaladdress';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function homeMdb()
+    {
+        return 'homemdb';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function homeDrive()
+    {
+        return 'homeDrive';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function homeDirectory()
+    {
+        return 'homeDirectory';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function info()
+    {
+        return 'info';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function initials()
+    {
+        return 'initials';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function instanceType()
+    {
+        return 'instancetype';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCriticalSystemObject()
+    {
+        return 'iscriticalsystemobject';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function jpegPhoto()
+    {
+        return 'jpegphoto';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lastLogOff()
+    {
+        return 'lastlogoff';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lastLogOn()
+    {
+        return 'lastlogon';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lastLogOnTimestamp()
+    {
+        return 'lastlogontimestamp';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lastName()
+    {
+        return 'sn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function legacyExchangeDn()
+    {
+        return 'legacyexchangedn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function locale()
+    {
+        return 'l';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function location()
+    {
+        return 'location';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lockoutTime()
+    {
+        return 'lockouttime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function manager()
+    {
+        return 'manager';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function maxPasswordAge()
+    {
+        return 'maxpwdage';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function memberOf()
+    {
+        return 'memberof';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function memberOfRecursive()
+    {
+        return 'memberof:1.2.840.113556.1.4.1941:';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function memberRange($from, $to)
+    {
+        return $this->member() . ";range={$from}-{$to}";
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function member()
+    {
+        return 'member';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function messageTrackingEnabled()
+    {
+        return 'messagetrackingenabled';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function msExchangeServer()
+    {
+        return 'ms-exch-exchange-server';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function name()
+    {
+        return 'name';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function neverExpiresDate()
+    {
+        return '9223372036854775807';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategory()
+    {
+        return 'objectcategory';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryComputer()
+    {
+        return 'computer';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryContainer()
+    {
+        return 'container';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryExchangePrivateMdb()
+    {
+        return 'msexchprivatemdb';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryExchangeServer()
+    {
+        return 'msExchExchangeServer';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryExchangeStorageGroup()
+    {
+        return 'msExchStorageGroup';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryGroup()
+    {
+        return 'group';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryOrganizationalUnit()
+    {
+        return 'organizational-unit';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryPerson()
+    {
+        return 'person';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategoryPrinter()
+    {
+        return 'print-queue';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClass()
+    {
+        return 'objectclass';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassComputer()
+    {
+        return 'computer';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassContact()
+    {
+        return 'contact';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassContainer()
+    {
+        return 'container';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassGroup()
+    {
+        return 'group';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassOu()
+    {
+        return 'organizationalunit';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassPerson()
+    {
+        return 'person';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassPrinter()
+    {
+        return 'printqueue';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassUser()
+    {
+        return 'user';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectGuid()
+    {
+        return 'objectguid';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectGuidRequiresConversion()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectSid()
+    {
+        return 'objectsid';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectSidRequiresConversion()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function operatingSystem()
+    {
+        return 'operatingsystem';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function operatingSystemServicePack()
+    {
+        return 'operatingsystemservicepack';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function operatingSystemVersion()
+    {
+        return 'operatingsystemversion';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function organizationName()
+    {
+        return 'o';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function organizationalPerson()
+    {
+        return 'organizationalperson';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function organizationalUnit()
+    {
+        return 'organizationalunit';
+    }
+
+    /**
+     * The class name of the Organizational Unit model.
+     *
+     * @return string
+     */
+    public function organizationalUnitModel()
+    {
+        return OrganizationalUnit::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function organizationalUnitShort()
+    {
+        return 'ou';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function otherMailbox()
+    {
+        return 'othermailbox';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function passwordLastSet()
+    {
+        return 'pwdlastset';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function person()
+    {
+        return 'person';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function personalTitle()
+    {
+        return 'personaltitle';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function physicalDeliveryOfficeName()
+    {
+        return 'physicaldeliveryofficename';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function portName()
+    {
+        return 'portname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function postalCode()
+    {
+        return 'postalcode';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function primaryGroupId()
+    {
+        return 'primarygroupid';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerBinNames()
+    {
+        return 'printbinnames';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerColorSupported()
+    {
+        return 'printcolor';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerDuplexSupported()
+    {
+        return 'printduplexsupported';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerEndTime()
+    {
+        return 'printendtime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerMaxResolutionSupported()
+    {
+        return 'printmaxresolutionsupported';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerMediaSupported()
+    {
+        return 'printmediasupported';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerMemory()
+    {
+        return 'printmemory';
+    }
+
+    /**
+     * The class name of the Printer model.
+     *
+     * @return string
+     */
+    public function printerModel()
+    {
+        return Printer::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerName()
+    {
+        return 'printername';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerOrientationSupported()
+    {
+        return 'printorientationssupported';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerPrintRate()
+    {
+        return 'printrate';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerPrintRateUnit()
+    {
+        return 'printrateunit';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerShareName()
+    {
+        return 'printsharename';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerStaplingSupported()
+    {
+        return 'printstaplingsupported';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function printerStartTime()
+    {
+        return 'printstarttime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function priority()
+    {
+        return 'priority';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function profilePath()
+    {
+        return 'profilepath';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function proxyAddresses()
+    {
+        return 'proxyaddresses';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function roomNumber()
+    {
+        return 'roomnumber';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rootDomainNamingContext()
+    {
+        return 'rootdomainnamingcontext';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function schemaNamingContext()
+    {
+        return 'schemanamingcontext';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function scriptPath()
+    {
+        return 'scriptpath';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function serialNumber()
+    {
+        return 'serialnumber';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function serverName()
+    {
+        return 'servername';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function showInAddressBook()
+    {
+        return 'showinaddressbook';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function street()
+    {
+        return 'street';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function streetAddress()
+    {
+        return 'streetaddress';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function systemFlags()
+    {
+        return 'systemflags';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function telephone()
+    {
+        return 'telephonenumber';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function thumbnail()
+    {
+        return 'thumbnailphoto';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function title()
+    {
+        return 'title';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function top()
+    {
+        return 'top';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function true()
+    {
+        return 'TRUE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function unicodePassword()
+    {
+        return 'unicodepwd';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function updatedAt()
+    {
+        return 'whenchanged';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function url()
+    {
+        return 'url';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function user()
+    {
+        return 'user';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function userAccountControl()
+    {
+        return 'useraccountcontrol';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function userId()
+    {
+        return 'uid';
+    }
+
+    /**
+     * The class name of the User model.
+     *
+     * @return string
+     */
+    public function userModel()
+    {
+        return User::class;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function userPrincipalName()
+    {
+        return 'userprincipalname';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function versionNumber()
+    {
+        return 'versionnumber';
+    }
+}

+ 30 - 0
api/vendor/adldap2/adldap2/src/Schemas/FreeIPA.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Adldap\Schemas;
+
+class FreeIPA extends ActiveDirectory
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function distinguishedName()
+    {
+        return 'dn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategory()
+    {
+        return 'objectclass';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassGroup()
+    {
+        return 'ipausergroup';
+    }
+}

+ 94 - 0
api/vendor/adldap2/adldap2/src/Schemas/OpenLDAP.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace Adldap\Schemas;
+
+class OpenLDAP extends ActiveDirectory
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function distinguishedName()
+    {
+        return 'dn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function distinguishedNameSubKey()
+    {
+        //
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function filterEnabled()
+    {
+        return sprintf('(!(%s=*))', $this->lockoutTime());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function filterDisabled()
+    {
+        return sprintf('(%s=*)', $this->lockoutTime());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function lockoutTime()
+    {
+        return 'pwdAccountLockedTime';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectCategory()
+    {
+        return 'objectclass';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassGroup()
+    {
+        return 'groupofnames';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassOu()
+    {
+        return 'groupofuniquenames';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectClassPerson()
+    {
+        return 'inetorgperson';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectGuid()
+    {
+        return 'entryuuid';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function objectGuidRequiresConversion()
+    {
+        return false;
+    }
+}

+ 1323 - 0
api/vendor/adldap2/adldap2/src/Schemas/SchemaInterface.php

@@ -0,0 +1,1323 @@
+<?php
+
+namespace Adldap\Schemas;
+
+interface SchemaInterface
+{
+    /**
+     * The date when the account expires. This value represents the number of 100-nanosecond
+     * intervals since January 1, 1601 (UTC). A value of 0 or 0x7FFFFFFFFFFFFFFF
+     * (9223372036854775807) indicates that the account never expires.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675098(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function accountExpires();
+
+    /**
+     * The logon name used to support clients and servers running earlier versions of the
+     * operating system, such as Windows NT 4.0, Windows 95, Windows 98,
+     * and LAN Manager. This attribute must be 20 characters or
+     * less to support earlier clients.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679635(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function accountName();
+
+    /**
+     * This attribute contains information about every account type object.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679637(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function accountType();
+
+    /**
+     * The name to be displayed on admin screens.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675214(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function adminDisplayName();
+
+    /**
+     * Ambiguous name resolution attribute to be used when choosing between objects.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675223(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function anr();
+
+    /**
+     * The number of times the user tried to log on to the account using
+     * an incorrect password. A value of 0 indicates that the
+     * value is unknown.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675244(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function badPasswordCount();
+
+    /**
+     * The last time and date that an attempt to log on to this
+     * account was made with a password that is not valid.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675243(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function badPasswordTime();
+
+    /**
+     * The name that represents an object.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675449(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function commonName();
+
+    /**
+     * The user's company name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675457(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function company();
+
+    /**
+     * The object class computer string.
+     *
+     * Used when constructing new Computer models.
+     *
+     * @return string
+     */
+    public function computer();
+
+    /**
+     * The class name of the Computer model.
+     *
+     * @return string
+     */
+    public function computerModel();
+
+    /**
+     * DN enterprise configuration naming context.
+     *
+     * @link https://support.microsoft.com/en-us/kb/219005
+     *
+     * @return string
+     */
+    public function configurationNamingContext();
+
+    /**
+     * The object class contact string.
+     *
+     * Used when constructing new User models.
+     *
+     * @return string
+     */
+    public function contact();
+
+    /**
+     * The class name of the Contact model.
+     *
+     * @return string
+     */
+    public function contactModel();
+
+    /**
+     * The class name of the Container model.
+     *
+     * @return string
+     */
+    public function containerModel();
+
+    /**
+     * The entry's country attribute.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675432(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function country();
+
+    /**
+     * The entry's created at attribute.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680924(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function createdAt();
+
+    /**
+     * The entry's current time attribute.
+     *
+     * This attribute is only available with the Root DSE record.
+     *
+     * @return string
+     */
+    public function currentTime();
+
+    /**
+     * This is the default NC for a particular server.
+     *
+     * By default, the DN for the domain of which this directory server is a member.
+     *
+     * @link https://support.microsoft.com/en-us/kb/219005
+     *
+     * @return string
+     */
+    public function defaultNamingContext();
+
+    /**
+     * Contains the name for the department in which the user works.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675490(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function department();
+
+    /**
+     * Identifies a department within an organization.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675491(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function departmentNumber();
+
+    /**
+     * Contains the description to display for an object. This value is restricted
+     * as single-valued for backward compatibility in some cases but
+     * is allowed to be multi-valued in others.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675492(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function description();
+
+    /**
+     * The display name for an object. This is usually the combination
+     * of the users first name, middle initial, and last name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675514(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function displayName();
+
+    /**
+     * The LDAP API references an LDAP object by its distinguished name (DN).
+     *
+     * A DN is a sequence of relative distinguished names (RDN) connected by commas.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function distinguishedName();
+
+    /**
+     * The LDAP API references an LDAP object by its distinguished name (DN).
+     *
+     * Different vendors expect the value of the distinguished name to be in
+     * different places. For example ActiveDirectory expects distinguishedname
+     * value to be the first element in an array, however OpenLDAP expects
+     * the dn attribute to contain the value, not an array.
+     *
+     * @return int|null
+     */
+    public function distinguishedNameSubKey();
+
+    /**
+     * Name of computer as registered in DNS.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675524(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function dnsHostName();
+
+    /**
+     * Domain Component located inside an RDN.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/aa366101(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function domainComponent();
+
+    /**
+     * The device driver name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675652(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function driverName();
+
+    /**
+     * The Version number of device driver.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675653(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function driverVersion();
+
+    /**
+     * The list of email addresses for a contact.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676855(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function email();
+
+    /**
+     * The email nickname for the user.
+     *
+     * @return string
+     */
+    public function emailNickname();
+
+    /**
+     * The ID of an employee.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675662(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function employeeId();
+
+    /**
+     * The number assigned to an employee other than the ID.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675663(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function employeeNumber();
+
+    /**
+     * The job category for an employee.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675664(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function employeeType();
+
+    /**
+     * The class name of the Entry model.
+     *
+     * @return string
+     */
+    public function entryModel();
+
+    /**
+     * The LDAP `false` boolean in string form for conversion.
+     *
+     * @return string
+     */
+    public function false();
+
+    /**
+     * The LDAP filter to query for enabled users.
+     *
+     * @return mixed
+     */
+    public function filterEnabled();
+
+    /**
+     * The LDAP filter to query for disabled users.
+     *
+     * @return mixed
+     */
+    public function filterDisabled();
+
+    /**
+     * Contains the given name (first name) of the user.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675719(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function firstName();
+
+    /**
+     * The class name of the Group model.
+     *
+     * @return string
+     */
+    public function groupModel();
+
+    /**
+     * Contains a set of flags that define the type and scope of a group object.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms675935(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function groupType();
+
+    /**
+     * A user's home address.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676193(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function homeAddress();
+
+    /**
+     * The users mailbox database location.
+     *
+     * @return string
+     */
+    public function homeMdb();
+
+    /**
+     * Specifies the drive letter to which to map the UNC path specified by homeDirectory.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676191(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function homeDrive();
+
+    /**
+     * The home directory for the account.
+     * 
+     * @link https://msdn.microsoft.com/en-us/library/ms676190(v=vs.85).aspx
+     *
+     * @return string|null
+     */
+    public function homeDirectory();
+
+    /**
+     * The users extra notable information.
+     *
+     * @return string
+     */
+    public function info();
+
+    /**
+     * Contains the initials for parts of the user's full name.
+     *
+     * This may be used as the middle initial in the Windows Address Book.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676202(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function initials();
+
+    /**
+     * A bitfield that dictates how the object is instantiated on a particular server.
+     *
+     * The value of this attribute can differ on different replicas even if the replicas are in sync.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676204(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function instanceType();
+
+    /**
+     * If TRUE, the object hosting this attribute must be replicated during installation of a new replica.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676798(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function isCriticalSystemObject();
+
+    /**
+     * Used to store one or more images of a person using the JPEG File Interchange Format [JFIF].
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676813(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function jpegPhoto();
+
+    /**
+     * This attribute is not used.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676822(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function lastLogOff();
+
+    /**
+     * The last time the user logged on. This value is stored as a large integer that
+     * represents the number of 100-nanosecond intervals since January 1, 1601 (UTC).
+     *
+     * A value of zero means that the last logon time is unknown.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676823(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function lastLogOn();
+
+    /**
+     * This is the time that the user last logged into the domain.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676824(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function lastLogOnTimestamp();
+
+    /**
+     * This attribute contains the family or last name for a user.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679872(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function lastName();
+
+    /**
+     * The distinguished name previously used by Exchange.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676830(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function legacyExchangeDn();
+
+    /**
+     * The users locale.
+     *
+     * @return string
+     */
+    public function locale();
+
+    /**
+     * The user's location, such as office number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676839(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function location();
+
+    /**
+     * The date and time (UTC) that this account was locked out. This value is stored
+     * as a large integer that represents the number of 100-nanosecond intervals
+     * since January 1, 1601 (UTC). A value of zero means that the
+     * account is not currently locked out.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676843(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function lockoutTime();
+
+    /**
+     * Contains the distinguished name of the user who is the user's manager.
+     *
+     * The manager's user object contains a directReports property that
+     * contains references to all user objects that have their manager
+     * properties set to this distinguished name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676859(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function manager();
+
+    /**
+     * The maximum amount of time, in 100-nanosecond intervals, a password is valid.
+     *
+     * This value is stored as a large integer that represents the number of
+     * 100-nanosecond intervals from the time the password was set
+     * before the password expires.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms676863(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function maxPasswordAge();
+
+    /**
+     * The list of users that belong to the group.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677097(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function member();
+
+    /**
+     * The distinguished names of the groups to which this object belongs.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677099(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function memberOf();
+
+    /**
+     * The distinguished names of the groups to which this object belongs.
+     *
+     * This string contains a rule OID indicating the inclusion of ancestral and child members.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677099(v=vs.85).aspx
+     * @link https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function memberOfRecursive();
+
+    /**
+     * The range limited list of users that belong to the group. See range limit in Active Directory
+     * (Range Retrieval of Attribute Values https://msdn.microsoft.com/en-us/library/cc223242.aspx)
+     * Issue #342
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms677097(v=vs.85).aspx
+     *
+     * @param string|int $from
+     * @param string|int $to
+     *
+     * @return string
+     */
+    public function memberRange($from, $to);
+
+    /**
+     * @link https://msdn.microsoft.com/en-us/library/ms981934(v=exchg.65).aspx
+     *
+     * @return string
+     */
+    public function messageTrackingEnabled();
+
+    /**
+     * The object category of an exchange server.
+     *
+     * @return string
+     */
+    public function msExchangeServer();
+
+    /**
+     * The general name of the entry.
+     *
+     * @return string
+     */
+    public function name();
+
+    /**
+     * Returns a string value indicating that an account does not expire.
+     *
+     * @return string
+     */
+    public function neverExpiresDate();
+
+    /**
+     * An object class name used to group objects of this or derived classes.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679011(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function objectCategory();
+
+    /**
+     * The computer object category.
+     *
+     * @return string
+     */
+    public function objectCategoryComputer();
+
+    /**
+     * The container object category.
+     *
+     * @return string
+     */
+    public function objectCategoryContainer();
+
+    /**
+     * The exchange private MDB category.
+     *
+     * @return string
+     */
+    public function objectCategoryExchangePrivateMdb();
+
+    /**
+     * The exchange server object category.
+     *
+     * @return string
+     */
+    public function objectCategoryExchangeServer();
+
+    /**
+     * The exchange storage group object category.
+     *
+     * @return string
+     */
+    public function objectCategoryExchangeStorageGroup();
+
+    /**
+     * The group object category.
+     *
+     * @return string
+     */
+    public function objectCategoryGroup();
+
+    /**
+     * The organizational unit category.
+     *
+     * @return string
+     */
+    public function objectCategoryOrganizationalUnit();
+
+    /**
+     * The person object category.
+     *
+     * @return string
+     */
+    public function objectCategoryPerson();
+
+    /**
+     * The printer object category.
+     *
+     * @return string
+     */
+    public function objectCategoryPrinter();
+
+    /**
+     * The list of classes from which this class is derived.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679012(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function objectClass();
+
+    /**
+     * The computer object class.
+     *
+     * @return string
+     */
+    public function objectClassComputer();
+
+    /**
+     * The contact object class.
+     *
+     * @return string
+     */
+    public function objectClassContact();
+
+    /**
+     * The container object class.
+     *
+     * @return string
+     */
+    public function objectClassContainer();
+
+    /**
+     * The group object class.
+     *
+     * @return string
+     */
+    public function objectClassGroup();
+
+    /**
+     * The ou object class.
+     *
+     * @return string
+     */
+    public function objectClassOu();
+
+    /**
+     * The person object class.
+     *
+     * Represents people who are associated with an organization in some way.
+     *
+     * @return string
+     */
+    public function objectClassPerson();
+
+    /**
+     * The printer object class.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683911(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function objectClassPrinter();
+
+    /**
+     * The user object class.
+     *
+     * @return string
+     */
+    public function objectClassUser();
+
+    /**
+     * The unique identifier for an object.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679021(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function objectGuid();
+
+    /**
+     * Determine whether the object GUID requires conversion from binary.
+     *
+     * @return bool
+     */
+    public function objectGuidRequiresConversion();
+
+    /**
+     * A binary value that specifies the security identifier (SID) of the user.
+     *
+     * The SID is a unique value used to identify the user as a security principal.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679024(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function objectSid();
+
+    /**
+     * Determine whether the object SID requires conversion from binary.
+     *
+     * @return bool
+     */
+    public function objectSidRequiresConversion();
+
+    /**
+     * The Operating System name, for example, Windows Vista Enterprise.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679076(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function operatingSystem();
+
+    /**
+     * The operating system service pack ID string (for example, SP3).
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679078(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function operatingSystemServicePack();
+
+    /**
+     * The operating system version string, for example, 4.0.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679079(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function operatingSystemVersion();
+
+    /**
+     * The RDN version of organization name for use in distinguished names.
+     *
+     * @return mixed
+     */
+    public function organizationName();
+
+    /**
+     * This class is used for objects that contain organizational information about a user,
+     * such as the employee number, department, manager, title, office address, and so on.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683883(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function organizationalPerson();
+
+    /**
+     * A container for storing users, computers, and other account objects.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683886(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function organizationalUnit();
+
+    /**
+     * The class name of the Organizational Unit model.
+     *
+     * @return string
+     */
+    public function organizationalUnitModel();
+
+    /**
+     * The RDN version of organizational unit for use in distinguished names.
+     *
+     * @return string
+     */
+    public function organizationalUnitShort();
+
+    /**
+     * Contains other additional mail addresses in a form such as CCMAIL: JohnDoe.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679091(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function otherMailbox();
+
+    /**
+     * The date and time that the password for this account was last changed.
+     *
+     * This value is stored as a large integer that represents the number of 100 nanosecond intervals
+     * since January 1, 1601 (UTC). If this value is set to 0 and the User-Account-Control attribute
+     * does not contain the UF_DONT_EXPIRE_PASSWD flag, then the user must set the password at
+     * the next logon.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679430(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function passwordLastSet();
+
+    /**
+     * The person object class.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683895(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function person();
+
+    /**
+     * The user's title.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679115(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function personalTitle();
+
+    /**
+     * Contains the office location in the user's place of business.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679117(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function physicalDeliveryOfficeName();
+
+    /**
+     * List of port names. For example, for printer ports or comm ports.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679131(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function portName();
+
+    /**
+     * The postal or zip code for mail delivery.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679366(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function postalCode();
+
+    /**
+     * Contains the relative identifier (RID) for the primary group of the user.
+     *
+     * By default, this is the RID for the Domain Users group.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679375(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function primaryGroupId();
+
+    /**
+     * A list of printer bin names.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679380(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerBinNames();
+
+    /**
+     * If a printer can print in color.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679382(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerColorSupported();
+
+    /**
+     * Indicates the type of duplex support a printer has.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679383(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerDuplexSupported();
+
+    /**
+     * The time a print queue stops servicing jobs.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679384(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerEndTime();
+
+    /**
+     * The maximum printer resolution.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679391(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerMaxResolutionSupported();
+
+    /**
+     * A list of media supported by a printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679395(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerMediaSupported();
+
+    /**
+     * The amount of memory installed in a printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679396(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerMemory();
+
+    /**
+     * The class name of the Printer model.
+     *
+     * @return string
+     */
+    public function printerModel();
+
+    /**
+     * The display name of an attached printer.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679385(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerName();
+
+    /**
+     * The page rotation for landscape printing.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679402(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerOrientationSupported();
+
+    /**
+     * Driver-supplied print rate.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679405(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerPrintRate();
+
+    /**
+     * Driver-supplied print rate unit.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679406(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerPrintRateUnit();
+
+    /**
+     * The printer's share name.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679408(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerShareName();
+
+    /**
+     * If the printer supports stapling. Supplied by the driver.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679410(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerStaplingSupported();
+
+    /**
+     * The time a print queue begins servicing jobs.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679411(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function printerStartTime();
+
+    /**
+     * The current priority (of a process, print job, and so on).
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679413(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function priority();
+
+    /**
+     * Specifies a path to the user's profile. This value can be a null
+     * string, a local absolute path, or a UNC path.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679422(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function profilePath();
+
+    /**
+     * A proxy address is the address by which a Microsoft Exchange Server recipient
+     * object is recognized in a foreign mail system. Proxy addresses are required
+     * for all recipient objects, such as custom recipients and distribution lists.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679424(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function proxyAddresses();
+
+    /**
+     * The room number of an object.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679615(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function roomNumber();
+
+    /**
+     * The DN of the root domain NC for this DC's forest.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/cc223262.aspx
+     *
+     * @return mixed
+     */
+    public function rootDomainNamingContext();
+
+    /**
+     * The attribute.
+     *
+     * @return mixed
+     */
+    public function schemaNamingContext();
+
+    /**
+     * This attribute specifies the path for the user's logon script. The string can be null.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679656(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function scriptPath();
+
+    /**
+     * Part of X.500 specification. Not used by Active Directory.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679771(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function serialNumber();
+
+    /**
+     * The name of a server.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679772(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function serverName();
+
+    /**
+     * This attribute is used to indicate in which MAPI address books an object will appear.
+     *
+     * It is usually maintained by the Exchange Recipient Update Service.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679822(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function showInAddressBook();
+
+    /**
+     * The street address.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679882(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function street();
+
+    /**
+     * The street address.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms679882(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function streetAddress();
+
+    /**
+     * An integer value that contains flags that define additional properties of the class.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680022(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function systemFlags();
+
+    /**
+     * The primary telephone number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680027(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function telephone();
+
+    /**
+     * The users thumbnail photo path.
+     *
+     * @return string
+     */
+    public function thumbnail();
+
+    /**
+     * Contains the user's job title.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680037(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function title();
+
+    /**
+     * The top level class from which all classes are derived.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683975(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function top();
+
+    /**
+     * The LDAP `true` boolean in string form for conversion.
+     *
+     * @return string
+     */
+    public function true();
+
+    /**
+     * The password of the user in Windows NT one-way format (OWF). Windows 2000 uses the Windows NT OWF.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680513(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function unicodePassword();
+
+    /**
+     * The date when this object was last changed.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680921(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function updatedAt();
+
+    /**
+     * The entry's URL attribute.
+     *
+     * @return string
+     */
+    public function url();
+
+    /**
+     * The user object class.
+     *
+     * This class is used to store information about an employee or contractor who works for an organization.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms683980(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function user();
+
+    /**
+     * Flags that control the behavior of the user account.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680832(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function userAccountControl();
+
+    /**
+     * The user ID attribute.
+     *
+     * @return string
+     */
+    public function userId();
+
+    /**
+     * The class name of the User model.
+     *
+     * @return string
+     */
+    public function userModel();
+
+    /**
+     * This attribute contains the UPN that is an Internet-style login name for
+     * a user based on the Internet standard RFC 822.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680857(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function userPrincipalName();
+
+    /**
+     * A general purpose version number.
+     *
+     * @link https://msdn.microsoft.com/en-us/library/ms680897(v=vs.85).aspx
+     *
+     * @return string
+     */
+    public function versionNumber();
+}

+ 344 - 0
api/vendor/adldap2/adldap2/src/Utilities.php

@@ -0,0 +1,344 @@
+<?php
+
+namespace Adldap;
+
+class Utilities
+{
+    /**
+     * Converts a DN string into an array of RDNs.
+     *
+     * This will also decode hex characters into their true
+     * UTF-8 representation embedded inside the DN as well.
+     *
+     * @param string $dn
+     * @param bool   $removeAttributePrefixes
+     *
+     * @return array|false
+     */
+    public static function explodeDn($dn, $removeAttributePrefixes = true)
+    {
+        $dn = ldap_explode_dn($dn, ($removeAttributePrefixes ? 1 : 0));
+
+        if (is_array($dn) && array_key_exists('count', $dn)) {
+            foreach ($dn as $rdn => $value) {
+                $dn[$rdn] = self::unescape($value);
+            }
+        }
+
+        return $dn;
+    }
+
+    /**
+     * Returns true / false if the current
+     * PHP install supports escaping values.
+     *
+     * @return bool
+     */
+    public static function isEscapingSupported()
+    {
+        return function_exists('ldap_escape');
+    }
+
+    /**
+     * Returns an escaped string for use in an LDAP filter.
+     *
+     * @param string $value
+     * @param string $ignore
+     * @param $flags
+     *
+     * @return string
+     */
+    public static function escape($value, $ignore = '', $flags = 0)
+    {
+        if (!static::isEscapingSupported()) {
+            return static::escapeManual($value, $ignore, $flags);
+        }
+
+        return ldap_escape($value, $ignore, $flags);
+    }
+
+    /**
+     * Escapes the inserted value for LDAP.
+     *
+     * @param string $value
+     * @param string $ignore
+     * @param int    $flags
+     *
+     * @return string
+     */
+    protected static function escapeManual($value, $ignore = '', $flags = 0)
+    {
+        // If a flag was supplied, we'll send the value off
+        // to be escaped using the PHP flag values
+        // and return the result.
+        if ($flags) {
+            return static::escapeManualWithFlags($value, $ignore, $flags);
+        }
+
+        // Convert ignore string into an array.
+        $ignores = static::ignoreStrToArray($ignore);
+
+        // Convert the value to a hex string.
+        $hex = bin2hex($value);
+
+        // Separate the string, with the hex length of 2, and
+        // place a backslash on the end of each section.
+        $value = chunk_split($hex, 2, '\\');
+
+        // We'll append a backslash at the front of the string
+        // and remove the ending backslash of the string.
+        $value = '\\'.substr($value, 0, -1);
+
+        // Go through each character to ignore.
+        foreach ($ignores as $charToIgnore) {
+            // Convert the character to ignore to a hex.
+            $hexed = bin2hex($charToIgnore);
+
+            // Replace the hexed variant with the original character.
+            $value = str_replace('\\'.$hexed, $charToIgnore, $value);
+        }
+
+        // Finally we can return the escaped value.
+        return $value;
+    }
+
+    /**
+     * Escapes the inserted value with flags. Supplying either 1
+     * or 2 into the flags parameter will escape only certain values.
+     *
+     * @param string $value
+     * @param string $ignore
+     * @param int    $flags
+     *
+     * @return string
+     */
+    protected static function escapeManualWithFlags($value, $ignore = '', $flags = 0)
+    {
+        // Convert ignore string into an array
+        $ignores = static::ignoreStrToArray($ignore);
+
+        // The escape characters for search filters
+        $escapeFilter = ['\\', '*', '(', ')'];
+
+        // The escape characters for distinguished names
+        $escapeDn = ['\\', ',', '=', '+', '<', '>', ';', '"', '#'];
+
+        switch ($flags) {
+            case 1:
+                // Int 1 equals to LDAP_ESCAPE_FILTER
+                $escapes = $escapeFilter;
+                break;
+            case 2:
+                // Int 2 equals to LDAP_ESCAPE_DN
+                $escapes = $escapeDn;
+                break;
+            case 3:
+                // If both LDAP_ESCAPE_FILTER and LDAP_ESCAPE_DN are used
+                $escapes = array_unique(array_merge($escapeDn, $escapeFilter));
+                break;
+            default:
+                // We've been given an invalid flag, we'll escape everything to be safe.
+                return static::escapeManual($value, $ignore);
+        }
+
+        foreach ($escapes as $escape) {
+            // Make sure the escaped value isn't being ignored.
+            if (!in_array($escape, $ignores)) {
+                $hexed = static::escape($escape);
+
+                $value = str_replace($escape, $hexed, $value);
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * Un-escapes a hexadecimal string into
+     * its original string representation.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    public static function unescape($value)
+    {
+        $callback = function ($matches) {
+            return chr(hexdec($matches[1]));
+        };
+
+        return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/', $callback, $value);
+    }
+
+    /**
+     * Convert a binary SID to a string SID.
+     *
+     * @author Chad Sikorra
+     *
+     * @link https://github.com/ChadSikorra
+     * @link https://stackoverflow.com/questions/39533560/php-ldap-get-user-sid
+     *
+     * @param string $value The Binary SID
+     *
+     * @return string|null
+     */
+    public static function binarySidToString($value)
+    {
+        // Revision - 8bit unsigned int (C1)
+        // Count - 8bit unsigned int (C1)
+        // 2 null bytes
+        // ID - 32bit unsigned long, big-endian order
+        $sid = @unpack('C1rev/C1count/x2/N1id', $value);
+
+        if (!isset($sid['id']) || !isset($sid['rev'])) {
+            return;
+        }
+
+        $revisionLevel = $sid['rev'];
+
+        $identifierAuthority = $sid['id'];
+
+        $subs = isset($sid['count']) ? $sid['count'] : 0;
+
+        $sidHex = $subs ? bin2hex($value) : '';
+
+        $subAuthorities = [];
+
+        // The sub-authorities depend on the count, so only get as
+        // many as the count, regardless of data beyond it.
+        for ($i = 0; $i < $subs; $i++) {
+            $data = implode('', array_reverse(
+                str_split(
+                    substr($sidHex, 16 + ($i * 8), 8),
+                    2
+                )
+            ));
+
+            $subAuthorities[] = hexdec($data);
+        }
+
+        // Tack on the 'S-' and glue it all together...
+        return 'S-'.$revisionLevel.'-'.$identifierAuthority.implode(
+            preg_filter('/^/', '-', $subAuthorities)
+        );
+    }
+
+    /**
+     * Convert a binary GUID to a string GUID.
+     *
+     * @param string $binGuid
+     *
+     * @return string|null
+     */
+    public static function binaryGuidToString($binGuid)
+    {
+        if (trim($binGuid) == '' || is_null($binGuid)) {
+            return;
+        }
+
+        $hex = unpack('H*hex', $binGuid)['hex'];
+
+        $hex1 = substr($hex, -26, 2).substr($hex, -28, 2).substr($hex, -30, 2).substr($hex, -32, 2);
+        $hex2 = substr($hex, -22, 2).substr($hex, -24, 2);
+        $hex3 = substr($hex, -18, 2).substr($hex, -20, 2);
+        $hex4 = substr($hex, -16, 4);
+        $hex5 = substr($hex, -12, 12);
+
+        $guid = sprintf('%s-%s-%s-%s-%s', $hex1, $hex2, $hex3, $hex4, $hex5);
+
+        return $guid;
+    }
+
+    /**
+     * Converts a string GUID to it's hex variant.
+     *
+     * @param string $string
+     *
+     * @return string
+     */
+    public static function stringGuidToHex($string)
+    {
+        $hex = '\\' . substr($string, 6, 2) . '\\' . substr($string, 4, 2) . '\\' . substr($string, 2, 2) . '\\' . substr($string, 0, 2);
+        $hex = $hex . '\\' . substr($string, 11, 2) . '\\' . substr($string, 9, 2);
+        $hex = $hex . '\\' . substr($string, 16, 2) . '\\' . substr($string, 14, 2);
+        $hex = $hex . '\\' . substr($string, 19, 2) . '\\' . substr($string, 21, 2);
+        $hex = $hex . '\\' . substr($string, 24, 2) . '\\' . substr($string, 26, 2) . '\\' . substr($string, 28, 2) . '\\' . substr($string, 30, 2) . '\\' . substr($string, 32, 2) . '\\' . substr($string, 34, 2);
+
+        return $hex;
+    }
+
+    /**
+     * Encode a password for transmission over LDAP.
+     *
+     * @param string $password The password to encode
+     *
+     * @return string
+     */
+    public static function encodePassword($password)
+    {
+        return iconv('UTF-8', 'UTF-16LE', '"'.$password.'"');
+    }
+
+    /**
+     * Round a Windows timestamp down to seconds and remove
+     * the seconds between 1601-01-01 and 1970-01-01.
+     *
+     * @param float $windowsTime
+     *
+     * @return float
+     */
+    public static function convertWindowsTimeToUnixTime($windowsTime)
+    {
+        return round($windowsTime / 10000000) - 11644473600;
+    }
+
+    /**
+     * Convert a Unix timestamp to Windows timestamp.
+     *
+     * @param float $unixTime
+     *
+     * @return float
+     */
+    public static function convertUnixTimeToWindowsTime($unixTime)
+    {
+        return ($unixTime + 11644473600) * 10000000;
+    }
+
+    /**
+     * Validates that the inserted string is an object SID.
+     *
+     * @param string $sid
+     *
+     * @return bool
+     */
+    public static function isValidSid($sid)
+    {
+        return (bool) preg_match("/^S-\d(-\d{1,10}){1,16}$/i", $sid);
+    }
+
+    /**
+     * Validates that the inserted string is an object GUID.
+     *
+     * @param string $guid
+     *
+     * @return bool
+     */
+    public static function isValidGuid($guid)
+    {
+        return (bool) preg_match('/^([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}$/', $guid);
+    }
+
+    /**
+     * Converts an ignore string into an array.
+     *
+     * @param string $ignore
+     *
+     * @return array
+     */
+    protected static function ignoreStrToArray($ignore)
+    {
+        $ignore = trim($ignore);
+
+        return $ignore ? str_split($ignore) : [];
+    }
+}

+ 2 - 0
api/vendor/composer/autoload_files.php

@@ -6,5 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+    '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
     '0097ca414fcb37c7130ac24b05f485f8' => $vendorDir . '/dibi/dibi/src/loader.php',
 );

+ 9 - 0
api/vendor/composer/autoload_psr4.php

@@ -6,7 +6,16 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
+    'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
+    'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
+    'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
     'PHPMailer\\PHPMailer\\' => array($vendorDir . '/phpmailer/phpmailer/src'),
     'Lcobucci\\JWT\\' => array($vendorDir . '/lcobucci/jwt/src'),
+    'Illuminate\\Support\\' => array($vendorDir . '/illuminate/support'),
+    'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
+    'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
     'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
+    'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
+    'Adldap\\' => array($vendorDir . '/adldap2/adldap2/src'),
 );

+ 59 - 0
api/vendor/composer/autoload_static.php

@@ -7,25 +7,64 @@ namespace Composer\Autoload;
 class ComposerStaticInitcbdc783d76f8e7563dcce7d8af053ecb
 {
     public static $files = array (
+        '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+        '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
         '0097ca414fcb37c7130ac24b05f485f8' => __DIR__ . '/..' . '/dibi/dibi/src/loader.php',
     );
 
     public static $prefixLengthsPsr4 = array (
+        'S' => 
+        array (
+            'Symfony\\Polyfill\\Mbstring\\' => 26,
+            'Symfony\\Component\\Translation\\' => 30,
+        ),
         'P' => 
         array (
+            'Psr\\SimpleCache\\' => 16,
+            'Psr\\Container\\' => 14,
             'PHPMailer\\PHPMailer\\' => 20,
         ),
         'L' => 
         array (
             'Lcobucci\\JWT\\' => 13,
         ),
+        'I' => 
+        array (
+            'Illuminate\\Support\\' => 19,
+            'Illuminate\\Contracts\\' => 21,
+        ),
+        'D' => 
+        array (
+            'Doctrine\\Common\\Inflector\\' => 26,
+        ),
         'C' => 
         array (
             'Composer\\Semver\\' => 16,
+            'Carbon\\' => 7,
+        ),
+        'A' => 
+        array (
+            'Adldap\\' => 7,
         ),
     );
 
     public static $prefixDirsPsr4 = array (
+        'Symfony\\Polyfill\\Mbstring\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
+        ),
+        'Symfony\\Component\\Translation\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/symfony/translation',
+        ),
+        'Psr\\SimpleCache\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/simple-cache/src',
+        ),
+        'Psr\\Container\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/psr/container/src',
+        ),
         'PHPMailer\\PHPMailer\\' => 
         array (
             0 => __DIR__ . '/..' . '/phpmailer/phpmailer/src',
@@ -34,10 +73,30 @@ class ComposerStaticInitcbdc783d76f8e7563dcce7d8af053ecb
         array (
             0 => __DIR__ . '/..' . '/lcobucci/jwt/src',
         ),
+        'Illuminate\\Support\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/support',
+        ),
+        'Illuminate\\Contracts\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/illuminate/contracts',
+        ),
+        'Doctrine\\Common\\Inflector\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector',
+        ),
         'Composer\\Semver\\' => 
         array (
             0 => __DIR__ . '/..' . '/composer/semver/src',
         ),
+        'Carbon\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
+        ),
+        'Adldap\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/adldap2/adldap2/src',
+        ),
     );
 
     public static $prefixesPsr0 = array (

+ 515 - 0
api/vendor/composer/installed.json

@@ -310,5 +310,520 @@
             "iri",
             "sockets"
         ]
+    },
+    {
+        "name": "symfony/polyfill-mbstring",
+        "version": "v1.6.0",
+        "version_normalized": "1.6.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/polyfill-mbstring.git",
+            "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+            "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.3"
+        },
+        "suggest": {
+            "ext-mbstring": "For best performance"
+        },
+        "time": "2017-10-11T12:05:26+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.6-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Polyfill\\Mbstring\\": ""
+            },
+            "files": [
+                "bootstrap.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Nicolas Grekas",
+                "email": "p@tchwork.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony polyfill for the Mbstring extension",
+        "homepage": "https://symfony.com",
+        "keywords": [
+            "compatibility",
+            "mbstring",
+            "polyfill",
+            "portable",
+            "shim"
+        ]
+    },
+    {
+        "name": "symfony/translation",
+        "version": "v3.4.3",
+        "version_normalized": "3.4.3.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/symfony/translation.git",
+            "reference": "17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/symfony/translation/zipball/17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a",
+            "reference": "17b5962d252b2d6d1d37a2485ebb7ddc5b2bef0a",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^5.5.9|>=7.0.8",
+            "symfony/polyfill-mbstring": "~1.0"
+        },
+        "conflict": {
+            "symfony/config": "<2.8",
+            "symfony/dependency-injection": "<3.4",
+            "symfony/yaml": "<3.4"
+        },
+        "require-dev": {
+            "psr/log": "~1.0",
+            "symfony/config": "~2.8|~3.0|~4.0",
+            "symfony/dependency-injection": "~3.4|~4.0",
+            "symfony/finder": "~2.8|~3.0|~4.0",
+            "symfony/intl": "^2.8.18|^3.2.5|~4.0",
+            "symfony/yaml": "~3.4|~4.0"
+        },
+        "suggest": {
+            "psr/log": "To use logging capability in translator",
+            "symfony/config": "",
+            "symfony/yaml": ""
+        },
+        "time": "2018-01-03T07:37:34+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "3.4-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Symfony\\Component\\Translation\\": ""
+            },
+            "exclude-from-classmap": [
+                "/Tests/"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Fabien Potencier",
+                "email": "fabien@symfony.com"
+            },
+            {
+                "name": "Symfony Community",
+                "homepage": "https://symfony.com/contributors"
+            }
+        ],
+        "description": "Symfony Translation Component",
+        "homepage": "https://symfony.com"
+    },
+    {
+        "name": "nesbot/carbon",
+        "version": "1.22.1",
+        "version_normalized": "1.22.1.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/briannesbitt/Carbon.git",
+            "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+            "reference": "7cdf42c0b1cc763ab7e4c33c47a24e27c66bfccc",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0",
+            "symfony/translation": "~2.6 || ~3.0"
+        },
+        "require-dev": {
+            "friendsofphp/php-cs-fixer": "~2",
+            "phpunit/phpunit": "~4.0 || ~5.0"
+        },
+        "time": "2017-01-16T07:55:07+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.23-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Carbon\\": "src/Carbon/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Brian Nesbitt",
+                "email": "brian@nesbot.com",
+                "homepage": "http://nesbot.com"
+            }
+        ],
+        "description": "A simple API extension for DateTime.",
+        "homepage": "http://carbon.nesbot.com",
+        "keywords": [
+            "date",
+            "datetime",
+            "time"
+        ]
+    },
+    {
+        "name": "psr/simple-cache",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/simple-cache.git",
+            "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/753fa598e8f3b9966c886fe13f370baa45ef0e24",
+            "reference": "753fa598e8f3b9966c886fe13f370baa45ef0e24",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2017-01-02T13:31:39+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\SimpleCache\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common interfaces for simple caching",
+        "keywords": [
+            "cache",
+            "caching",
+            "psr",
+            "psr-16",
+            "simple-cache"
+        ]
+    },
+    {
+        "name": "psr/container",
+        "version": "1.0.0",
+        "version_normalized": "1.0.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/php-fig/container.git",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=5.3.0"
+        },
+        "time": "2017-02-14T16:28:37+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.0.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Psr\\Container\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "PHP-FIG",
+                "homepage": "http://www.php-fig.org/"
+            }
+        ],
+        "description": "Common Container Interface (PHP FIG PSR-11)",
+        "homepage": "https://github.com/php-fig/container",
+        "keywords": [
+            "PSR-11",
+            "container",
+            "container-interface",
+            "container-interop",
+            "psr"
+        ]
+    },
+    {
+        "name": "illuminate/contracts",
+        "version": "v5.5.28",
+        "version_normalized": "5.5.28.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/illuminate/contracts.git",
+            "reference": "03e9014d2091a30b025c895aa6d39c2755576ea5"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/illuminate/contracts/zipball/03e9014d2091a30b025c895aa6d39c2755576ea5",
+            "reference": "03e9014d2091a30b025c895aa6d39c2755576ea5",
+            "shasum": ""
+        },
+        "require": {
+            "php": ">=7.0",
+            "psr/container": "~1.0",
+            "psr/simple-cache": "~1.0"
+        },
+        "time": "2017-11-22T19:01:14+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "5.5-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Illuminate\\Contracts\\": ""
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Taylor Otwell",
+                "email": "taylor@laravel.com"
+            }
+        ],
+        "description": "The Illuminate Contracts package.",
+        "homepage": "https://laravel.com"
+    },
+    {
+        "name": "doctrine/inflector",
+        "version": "v1.3.0",
+        "version_normalized": "1.3.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/doctrine/inflector.git",
+            "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
+            "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^7.1"
+        },
+        "require-dev": {
+            "phpunit/phpunit": "^6.2"
+        },
+        "time": "2018-01-09T20:05:19+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "1.3.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Roman Borschel",
+                "email": "roman@code-factory.org"
+            },
+            {
+                "name": "Benjamin Eberlei",
+                "email": "kontakt@beberlei.de"
+            },
+            {
+                "name": "Guilherme Blanco",
+                "email": "guilhermeblanco@gmail.com"
+            },
+            {
+                "name": "Jonathan Wage",
+                "email": "jonwage@gmail.com"
+            },
+            {
+                "name": "Johannes Schmitt",
+                "email": "schmittjoh@gmail.com"
+            }
+        ],
+        "description": "Common String Manipulations with regard to casing and singular/plural rules.",
+        "homepage": "http://www.doctrine-project.org",
+        "keywords": [
+            "inflection",
+            "pluralize",
+            "singularize",
+            "string"
+        ]
+    },
+    {
+        "name": "illuminate/support",
+        "version": "v5.5.28",
+        "version_normalized": "5.5.28.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/illuminate/support.git",
+            "reference": "4db3cc82b483172b1b25d9dfcec684927f5c8cf9"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/illuminate/support/zipball/4db3cc82b483172b1b25d9dfcec684927f5c8cf9",
+            "reference": "4db3cc82b483172b1b25d9dfcec684927f5c8cf9",
+            "shasum": ""
+        },
+        "require": {
+            "doctrine/inflector": "~1.1",
+            "ext-mbstring": "*",
+            "illuminate/contracts": "5.5.*",
+            "nesbot/carbon": "^1.20",
+            "php": ">=7.0"
+        },
+        "replace": {
+            "tightenco/collect": "self.version"
+        },
+        "suggest": {
+            "illuminate/filesystem": "Required to use the composer class (5.2.*).",
+            "symfony/process": "Required to use the composer class (~3.3).",
+            "symfony/var-dumper": "Required to use the dd function (~3.3)."
+        },
+        "time": "2017-12-24T20:02:59+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "5.5-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Illuminate\\Support\\": ""
+            },
+            "files": [
+                "helpers.php"
+            ]
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Taylor Otwell",
+                "email": "taylor@laravel.com"
+            }
+        ],
+        "description": "The Illuminate Support package.",
+        "homepage": "https://laravel.com"
+    },
+    {
+        "name": "adldap2/adldap2",
+        "version": "v8.0.10",
+        "version_normalized": "8.0.10.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/Adldap2/Adldap2.git",
+            "reference": "5ab7df6f24bf952666e926ce5220a21325f8f277"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/5ab7df6f24bf952666e926ce5220a21325f8f277",
+            "reference": "5ab7df6f24bf952666e926ce5220a21325f8f277",
+            "shasum": ""
+        },
+        "require": {
+            "ext-ldap": "*",
+            "illuminate/support": "~5.0",
+            "php": ">=5.5.9"
+        },
+        "require-dev": {
+            "mockery/mockery": "~0.9|~1.0",
+            "phpunit/phpunit": "~4.8|~5.6"
+        },
+        "time": "2018-01-02T14:14:44+00:00",
+        "type": "library",
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Adldap\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Steve Bauman",
+                "email": "steven_bauman@outlook.com",
+                "role": "Developer"
+            }
+        ],
+        "description": "A PHP LDAP Package for humans.",
+        "keywords": [
+            "active directory",
+            "ad",
+            "adLDAP",
+            "adldap2",
+            "directory",
+            "ldap",
+            "windows"
+        ]
     }
 ]

+ 19 - 0
api/vendor/doctrine/inflector/LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2006-2015 Doctrine Project
+
+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.

+ 6 - 0
api/vendor/doctrine/inflector/README.md

@@ -0,0 +1,6 @@
+# Doctrine Inflector
+
+Doctrine Inflector is a small library that can perform string manipulations
+with regard to upper-/lowercase and singular/plural forms of words.
+
+[![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](https://travis-ci.org/doctrine/inflector)

+ 32 - 0
api/vendor/doctrine/inflector/composer.json

@@ -0,0 +1,32 @@
+{
+    "name": "doctrine/inflector",
+    "type": "library",
+    "description": "Common String Manipulations with regard to casing and singular/plural rules.",
+    "keywords": ["string", "inflection", "singularize", "pluralize"],
+    "homepage": "http://www.doctrine-project.org",
+    "license": "MIT",
+    "authors": [
+        {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
+        {"name": "Roman Borschel", "email": "roman@code-factory.org"},
+        {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
+        {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
+        {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
+    ],
+    "require": {
+        "php": "^7.1"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^6.2"
+    },
+    "autoload": {
+        "psr-4": { "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" }
+    },
+    "autoload-dev": {
+        "psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Tests/Common/Inflector" }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.3.x-dev"
+        }
+    }
+}

+ 490 - 0
api/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php

@@ -0,0 +1,490 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the MIT license. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\Common\Inflector;
+
+/**
+ * Doctrine inflector has static methods for inflecting text.
+ *
+ * The methods in these classes are from several different sources collected
+ * across several different php projects and several different authors. The
+ * original author names and emails are not known.
+ *
+ * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications.
+ *
+ * @link   www.doctrine-project.org
+ * @since  1.0
+ * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ */
+class Inflector
+{
+    /**
+     * Plural inflector rules.
+     *
+     * @var string[][]
+     */
+    private static $plural = array(
+        'rules' => array(
+            '/(s)tatus$/i' => '\1\2tatuses',
+            '/(quiz)$/i' => '\1zes',
+            '/^(ox)$/i' => '\1\2en',
+            '/([m|l])ouse$/i' => '\1ice',
+            '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
+            '/(x|ch|ss|sh)$/i' => '\1es',
+            '/([^aeiouy]|qu)y$/i' => '\1ies',
+            '/(hive|gulf)$/i' => '\1s',
+            '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+            '/sis$/i' => 'ses',
+            '/([ti])um$/i' => '\1a',
+            '/(c)riterion$/i' => '\1riteria',
+            '/(p)erson$/i' => '\1eople',
+            '/(m)an$/i' => '\1en',
+            '/(c)hild$/i' => '\1hildren',
+            '/(f)oot$/i' => '\1eet',
+            '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes',
+            '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
+            '/us$/i' => 'uses',
+            '/(alias)$/i' => '\1es',
+            '/(analys|ax|cris|test|thes)is$/i' => '\1es',
+            '/s$/' => 's',
+            '/^$/' => '',
+            '/$/' => 's',
+        ),
+        'uninflected' => array(
+            '.*[nrlm]ese',
+            '.*deer',
+            '.*fish',
+            '.*measles',
+            '.*ois',
+            '.*pox',
+            '.*sheep',
+            'people',
+            'cookie',
+            'police',
+        ),
+        'irregular' => array(
+            'atlas' => 'atlases',
+            'axe' => 'axes',
+            'beef' => 'beefs',
+            'brother' => 'brothers',
+            'cafe' => 'cafes',
+            'chateau' => 'chateaux',
+            'niveau' => 'niveaux',
+            'child' => 'children',
+            'cookie' => 'cookies',
+            'corpus' => 'corpuses',
+            'cow' => 'cows',
+            'criterion' => 'criteria',
+            'curriculum' => 'curricula',
+            'demo' => 'demos',
+            'domino' => 'dominoes',
+            'echo' => 'echoes',
+            'foot' => 'feet',
+            'fungus' => 'fungi',
+            'ganglion' => 'ganglions',
+            'genie' => 'genies',
+            'genus' => 'genera',
+            'goose' => 'geese',
+            'graffito' => 'graffiti',
+            'hippopotamus' => 'hippopotami',
+            'hoof' => 'hoofs',
+            'human' => 'humans',
+            'iris' => 'irises',
+            'larva' => 'larvae',
+            'leaf' => 'leaves',
+            'loaf' => 'loaves',
+            'man' => 'men',
+            'medium' => 'media',
+            'memorandum' => 'memoranda',
+            'money' => 'monies',
+            'mongoose' => 'mongooses',
+            'motto' => 'mottoes',
+            'move' => 'moves',
+            'mythos' => 'mythoi',
+            'niche' => 'niches',
+            'nucleus' => 'nuclei',
+            'numen' => 'numina',
+            'occiput' => 'occiputs',
+            'octopus' => 'octopuses',
+            'opus' => 'opuses',
+            'ox' => 'oxen',
+            'passerby' => 'passersby',
+            'penis' => 'penises',
+            'person' => 'people',
+            'plateau' => 'plateaux',
+            'runner-up' => 'runners-up',
+            'sex' => 'sexes',
+            'soliloquy' => 'soliloquies',
+            'son-in-law' => 'sons-in-law',
+            'syllabus' => 'syllabi',
+            'testis' => 'testes',
+            'thief' => 'thieves',
+            'tooth' => 'teeth',
+            'tornado' => 'tornadoes',
+            'trilby' => 'trilbys',
+            'turf' => 'turfs',
+            'valve' => 'valves',
+            'volcano' => 'volcanoes',
+        )
+    );
+
+    /**
+     * Singular inflector rules.
+     *
+     * @var string[][]
+     */
+    private static $singular = array(
+        'rules' => array(
+            '/(s)tatuses$/i' => '\1\2tatus',
+            '/^(.*)(menu)s$/i' => '\1\2',
+            '/(quiz)zes$/i' => '\\1',
+            '/(matr)ices$/i' => '\1ix',
+            '/(vert|ind)ices$/i' => '\1ex',
+            '/^(ox)en/i' => '\1',
+            '/(alias)(es)*$/i' => '\1',
+            '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o',
+            '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
+            '/([ftw]ax)es/i' => '\1',
+            '/(analys|ax|cris|test|thes)es$/i' => '\1is',
+            '/(shoe|slave)s$/i' => '\1',
+            '/(o)es$/i' => '\1',
+            '/ouses$/' => 'ouse',
+            '/([^a])uses$/' => '\1us',
+            '/([m|l])ice$/i' => '\1ouse',
+            '/(x|ch|ss|sh)es$/i' => '\1',
+            '/(m)ovies$/i' => '\1\2ovie',
+            '/(s)eries$/i' => '\1\2eries',
+            '/([^aeiouy]|qu)ies$/i' => '\1y',
+            '/([lr])ves$/i' => '\1f',
+            '/(tive)s$/i' => '\1',
+            '/(hive)s$/i' => '\1',
+            '/(drive)s$/i' => '\1',
+            '/(dive)s$/i' => '\1',
+            '/(olive)s$/i' => '\1',
+            '/([^fo])ves$/i' => '\1fe',
+            '/(^analy)ses$/i' => '\1sis',
+            '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+            '/(c)riteria$/i' => '\1riterion',
+            '/([ti])a$/i' => '\1um',
+            '/(p)eople$/i' => '\1\2erson',
+            '/(m)en$/i' => '\1an',
+            '/(c)hildren$/i' => '\1\2hild',
+            '/(f)eet$/i' => '\1oot',
+            '/(n)ews$/i' => '\1\2ews',
+            '/eaus$/' => 'eau',
+            '/^(.*us)$/' => '\\1',
+            '/s$/i' => '',
+        ),
+        'uninflected' => array(
+            '.*[nrlm]ese',
+            '.*deer',
+            '.*fish',
+            '.*measles',
+            '.*ois',
+            '.*pox',
+            '.*sheep',
+            '.*ss',
+            'data',
+            'police',
+            'pants',
+            'clothes',
+        ),
+        'irregular' => array(
+            'abuses'     => 'abuse',
+            'avalanches' => 'avalanche',
+            'caches'     => 'cache',
+            'criteria'   => 'criterion',
+            'curves'     => 'curve',
+            'emphases'   => 'emphasis',
+            'foes'       => 'foe',
+            'geese'      => 'goose',
+            'graves'     => 'grave',
+            'hoaxes'     => 'hoax',
+            'media'      => 'medium',
+            'neuroses'   => 'neurosis',
+            'waves'      => 'wave',
+            'oases'      => 'oasis',
+            'valves'     => 'valve',
+        )
+    );
+
+    /**
+     * Words that should not be inflected.
+     *
+     * @var array
+     */
+    private static $uninflected = array(
+        '.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches',
+        'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese',
+        'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland',
+        'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese',
+        'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold', 
+        'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi',
+        'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata',
+        'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring',
+        'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese',
+        'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass',
+        'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic',
+        'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese'
+    );
+
+    /**
+     * Method cache array.
+     *
+     * @var array
+     */
+    private static $cache = array();
+
+    /**
+     * The initial state of Inflector so reset() works.
+     *
+     * @var array
+     */
+    private static $initialState = array();
+
+    /**
+     * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
+     */
+    public static function tableize(string $word) : string
+    {
+        return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
+    }
+
+    /**
+     * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
+     */
+    public static function classify(string $word) : string
+    {
+        return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
+    }
+
+    /**
+     * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
+     */
+    public static function camelize(string $word) : string
+    {
+        return lcfirst(self::classify($word));
+    }
+
+    /**
+     * Uppercases words with configurable delimeters between words.
+     *
+     * Takes a string and capitalizes all of the words, like PHP's built-in
+     * ucwords function. This extends that behavior, however, by allowing the
+     * word delimeters to be configured, rather than only separating on
+     * whitespace.
+     *
+     * Here is an example:
+     * <code>
+     * <?php
+     * $string = 'top-o-the-morning to all_of_you!';
+     * echo \Doctrine\Common\Inflector\Inflector::ucwords($string);
+     * // Top-O-The-Morning To All_of_you!
+     *
+     * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ ');
+     * // Top-O-The-Morning To All_Of_You!
+     * ?>
+     * </code>
+     *
+     * @param string $string The string to operate on.
+     * @param string $delimiters A list of word separators.
+     *
+     * @return string The string with all delimeter-separated words capitalized.
+     */
+    public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string
+    {
+        return ucwords($string, $delimiters);
+    }
+
+    /**
+     * Clears Inflectors inflected value caches, and resets the inflection
+     * rules to the initial values.
+     */
+    public static function reset() : void
+    {
+        if (empty(self::$initialState)) {
+            self::$initialState = get_class_vars('Inflector');
+
+            return;
+        }
+
+        foreach (self::$initialState as $key => $val) {
+            if ($key !== 'initialState') {
+                self::${$key} = $val;
+            }
+        }
+    }
+
+    /**
+     * Adds custom inflection $rules, of either 'plural' or 'singular' $type.
+     *
+     * ### Usage:
+     *
+     * {{{
+     * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
+     * Inflector::rules('plural', array(
+     *     'rules' => array('/^(inflect)ors$/i' => '\1ables'),
+     *     'uninflected' => array('dontinflectme'),
+     *     'irregular' => array('red' => 'redlings')
+     * ));
+     * }}}
+     *
+     * @param string  $type         The type of inflection, either 'plural' or 'singular'
+     * @param array|iterable $rules An array of rules to be added.
+     * @param boolean $reset        If true, will unset default inflections for all
+     *                              new rules that are being defined in $rules.
+     *
+     * @return void
+     */
+    public static function rules(string $type, iterable $rules, bool $reset = false) : void
+    {
+        foreach ($rules as $rule => $pattern) {
+            if ( ! is_array($pattern)) {
+                continue;
+            }
+
+            if ($reset) {
+                self::${$type}[$rule] = $pattern;
+            } else {
+                self::${$type}[$rule] = ($rule === 'uninflected')
+                    ? array_merge($pattern, self::${$type}[$rule])
+                    : $pattern + self::${$type}[$rule];
+            }
+
+            unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
+
+            if (isset(self::${$type}['merged'][$rule])) {
+                unset(self::${$type}['merged'][$rule]);
+            }
+
+            if ($type === 'plural') {
+                self::$cache['pluralize'] = self::$cache['tableize'] = array();
+            } elseif ($type === 'singular') {
+                self::$cache['singularize'] = array();
+            }
+        }
+
+        self::${$type}['rules'] = $rules + self::${$type}['rules'];
+    }
+
+    /**
+     * Returns a word in plural form.
+     *
+     * @param string $word The word in singular form.
+     *
+     * @return string The word in plural form.
+     */
+    public static function pluralize(string $word) : string
+    {
+        if (isset(self::$cache['pluralize'][$word])) {
+            return self::$cache['pluralize'][$word];
+        }
+
+        if (!isset(self::$plural['merged']['irregular'])) {
+            self::$plural['merged']['irregular'] = self::$plural['irregular'];
+        }
+
+        if (!isset(self::$plural['merged']['uninflected'])) {
+            self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
+        }
+
+        if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
+            self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
+            self::$plural['cacheIrregular']   = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
+        }
+
+        if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
+            self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
+
+            return self::$cache['pluralize'][$word];
+        }
+
+        if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
+            self::$cache['pluralize'][$word] = $word;
+
+            return $word;
+        }
+
+        foreach (self::$plural['rules'] as $rule => $replacement) {
+            if (preg_match($rule, $word)) {
+                self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
+
+                return self::$cache['pluralize'][$word];
+            }
+        }
+    }
+
+    /**
+     * Returns a word in singular form.
+     *
+     * @param string $word The word in plural form.
+     *
+     * @return string The word in singular form.
+     */
+    public static function singularize(string $word) : string
+    {
+        if (isset(self::$cache['singularize'][$word])) {
+            return self::$cache['singularize'][$word];
+        }
+
+        if (!isset(self::$singular['merged']['uninflected'])) {
+            self::$singular['merged']['uninflected'] = array_merge(
+                self::$singular['uninflected'],
+                self::$uninflected
+            );
+        }
+
+        if (!isset(self::$singular['merged']['irregular'])) {
+            self::$singular['merged']['irregular'] = array_merge(
+                self::$singular['irregular'],
+                array_flip(self::$plural['irregular'])
+            );
+        }
+
+        if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
+            self::$singular['cacheUninflected'] = '(?:' . implode('|', self::$singular['merged']['uninflected']) . ')';
+            self::$singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$singular['merged']['irregular'])) . ')';
+        }
+
+        if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
+            self::$cache['singularize'][$word] = $regs[1] . $word[0] . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
+
+            return self::$cache['singularize'][$word];
+        }
+
+        if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
+            self::$cache['singularize'][$word] = $word;
+
+            return $word;
+        }
+
+        foreach (self::$singular['rules'] as $rule => $replacement) {
+            if (preg_match($rule, $word)) {
+                self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
+
+                return self::$cache['singularize'][$word];
+            }
+        }
+
+        self::$cache['singularize'][$word] = $word;
+
+        return $word;
+    }
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott