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