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\Protocol; 12 13use FreeDSx\Ldap\Operation\Request\AnonBindRequest; 14use FreeDSx\Ldap\Operation\Request\BindRequest; 15use FreeDSx\Ldap\Operation\Request\ExtendedRequest; 16use FreeDSx\Ldap\Operation\Request\RequestInterface; 17use FreeDSx\Ldap\Operation\Request\SearchRequest; 18use FreeDSx\Ldap\Operation\Request\SimpleBindRequest; 19use FreeDSx\Ldap\Operation\Request\UnbindRequest; 20use FreeDSx\Ldap\Server\Token\AnonToken; 21use FreeDSx\Ldap\Server\Token\BindToken; 22use FreeDSx\Ldap\Server\Token\TokenInterface; 23 24/** 25 * Abstracts out some of the server authorization logic. 26 * 27 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 28 */ 29class ServerAuthorization 30{ 31 /** 32 * @var bool 33 */ 34 protected $isAuthRequired; 35 36 /** 37 * @var bool 38 */ 39 protected $isAnonymousAllowed; 40 41 /** 42 * @var TokenInterface 43 */ 44 protected $token; 45 46 public function __construct(TokenInterface $token = null, array $options = []) 47 { 48 $this->token = $token ?? new AnonToken(); 49 $this->isAuthRequired = isset($options['require_authentication']) ? (bool) $options['require_authentication'] : true; 50 $this->isAnonymousAllowed = isset($options['allow_anonymous']) ? (bool) $options['allow_anonymous'] : false; 51 } 52 53 /** 54 * Helps determine if a specific request type actually requires authentication to complete. 55 * 56 * @param RequestInterface $request 57 * @return bool 58 */ 59 public function isAuthenticationRequired(RequestInterface $request): bool 60 { 61 if ($this->isAuthRequired === false) { 62 return false; 63 } 64 65 if ($request instanceof ExtendedRequest && $request->getName() === ExtendedRequest::OID_WHOAMI) { 66 return false; 67 } elseif ($request instanceof ExtendedRequest && $request->getName() === ExtendedRequest::OID_START_TLS) { 68 return false; 69 } elseif ($request instanceof UnbindRequest) { 70 return false; 71 } elseif ($request instanceof BindRequest) { 72 return false; 73 } elseif ($this->isRootDseSearch($request)) { 74 return false; 75 } 76 77 return true; 78 } 79 80 /** 81 * Determine if the bind type is actually supported. Anonymous binding may be disabled. 82 */ 83 public function isAuthenticationTypeSupported(RequestInterface $request): bool 84 { 85 if ($request instanceof AnonBindRequest) { 86 return $this->isAnonymousAllowed; 87 } 88 89 return $request instanceof SimpleBindRequest; 90 } 91 92 /** 93 * Determine if the incoming request is an authentication attempt. 94 */ 95 public function isAuthenticationRequest(RequestInterface $request): bool 96 { 97 return $request instanceof BindRequest; 98 } 99 100 /** 101 * Determine if the current token is "authenticated". In the case where authentication is not required, we always 102 * return true. 103 */ 104 public function isAuthenticated(): bool 105 { 106 if ($this->isAuthRequired === false) { 107 return true; 108 } 109 110 return $this->token instanceof BindToken; 111 } 112 113 /** 114 * Set the current token. 115 */ 116 public function setToken(TokenInterface $token): void 117 { 118 $this->token = $token; 119 } 120 121 /** 122 * Get the current token. 123 */ 124 public function getToken(): TokenInterface 125 { 126 return $this->token; 127 } 128 129 /** 130 * @param RequestInterface $request 131 * @return bool 132 */ 133 protected function isRootDseSearch(RequestInterface $request): bool 134 { 135 if (!$request instanceof SearchRequest) { 136 return false; 137 } 138 139 return $request->getScope() === SearchRequest::SCOPE_BASE_OBJECT 140 && ((string) $request->getBaseDn() === ''); 141 } 142} 143