1<?php 2/** 3 * This file is part of the FreeDSx LDAP package. 4 * 5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace FreeDSx\Ldap; 12 13use FreeDSx\Ldap\Exception\RuntimeException; 14use FreeDSx\Ldap\Server\RequestHandler\GenericRequestHandler; 15use FreeDSx\Ldap\Server\RequestHandler\RequestHandlerInterface; 16use FreeDSx\Ldap\Server\ServerRunner\PcntlServerRunner; 17use FreeDSx\Ldap\Server\ServerRunner\ServerRunnerInterface; 18use FreeDSx\Socket\SocketServer; 19 20/** 21 * The LDAP server. 22 * 23 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 24 */ 25class LdapServer 26{ 27 /** 28 * @var array 29 */ 30 protected $options = [ 31 'ip' => '0.0.0.0', 32 'port' => 389, 33 'idle_timeout' => 600, 34 'require_authentication' => true, 35 'allow_anonymous' => false, 36 'request_handler' => null, 37 'ssl_cert' => null, 38 'ssl_cert_passphrase' => null, 39 'dse_alt_server' => null, 40 'dse_naming_contexts' => 'dc=FreeDSx,dc=local', 41 'dse_vendor_name' => 'FreeDSx', 42 'dse_vendor_version' => null, 43 ]; 44 45 /** 46 * @var ServerRunnerInterface 47 */ 48 protected $runner; 49 50 /** 51 * @param array $options 52 * @param ServerRunnerInterface|null $serverRunner 53 */ 54 public function __construct(array $options = [], ServerRunnerInterface $serverRunner = null) 55 { 56 $this->options = array_merge($this->options, $options); 57 $this->validateRequestHandler(); 58 $this->runner = $serverRunner ?? new PcntlServerRunner($this->options); 59 } 60 61 /** 62 * Runs the LDAP server. Binds the socket on the request IP/port and sends it to the server runner. 63 */ 64 public function run(): void 65 { 66 $this->runner->run(SocketServer::bind($this->options['ip'], $this->options['port'], $this->options)); 67 } 68 69 /** 70 * The request handler should be constructed from a string class name. This is to make sure that each client instance 71 * has its own version of the handler to avoid conflicts and potential security issues sharing a request handler. 72 */ 73 protected function validateRequestHandler(): void 74 { 75 if (!isset($this->options['request_handler'])) { 76 $this->options['request_handler'] = GenericRequestHandler::class; 77 78 return; 79 } 80 if (!\is_string($this->options['request_handler'])) { 81 throw new RuntimeException(sprintf( 82 'The request handler must be a string class name, got %s.', 83 gettype($this->options['request_handler']) 84 )); 85 } 86 if (!\class_exists($this->options['request_handler'])) { 87 throw new RuntimeException(sprintf( 88 'The request handler class does not exist: %s', 89 $this->options['request_handler'] 90 )); 91 } 92 if (!\is_subclass_of($this->options['request_handler'], RequestHandlerInterface::class)) { 93 throw new RuntimeException(sprintf( 94 'The request handler class must implement "%s"', 95 RequestHandlerInterface::class 96 )); 97 } 98 } 99} 100