addBackend($authBackend); } } /** * Adds an authentication backend to the plugin. * * @param Backend\BackendInterface $authBackend * @return void */ function addBackend(Backend\BackendInterface $authBackend) { $this->backends[] = $authBackend; } /** * Initializes the plugin. This function is automatically called by the server * * @param Server $server * @return void */ function initialize(Server $server) { $server->on('beforeMethod', [$this, 'beforeMethod'], 10); } /** * Returns a plugin name. * * Using this name other plugins will be able to access other plugins * using DAV\Server::getPlugin * * @return string */ function getPluginName() { return 'auth'; } /** * Returns the currently logged-in principal. * * This will return a string such as: * * principals/username * principals/users/username * * This method will return null if nobody is logged in. * * @return string|null */ function getCurrentPrincipal() { return $this->currentPrincipal; } /** * This method is called before any HTTP method and forces users to be authenticated * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ function beforeMethod(RequestInterface $request, ResponseInterface $response) { if ($this->currentPrincipal) { // We already have authentication information. This means that the // event has already fired earlier, and is now likely fired for a // sub-request. // // We don't want to authenticate users twice, so we simply don't do // anything here. See Issue #700 for additional reasoning. // // This is not a perfect solution, but will be fixed once the // "currently authenticated principal" is information that's not // not associated with the plugin, but rather per-request. // // See issue #580 for more information about that. return; } $authResult = $this->check($request, $response); if ($authResult[0]) { // Auth was successful $this->currentPrincipal = $authResult[1]; $this->loginFailedReasons = null; return; } // If we got here, it means that no authentication backend was // successful in authenticating the user. $this->currentPrincipal = null; $this->loginFailedReasons = $authResult[1]; if ($this->autoRequireLogin) { $this->challenge($request, $response); throw new NotAuthenticated(implode(', ', $authResult[1])); } } /** * Checks authentication credentials, and logs the user in if possible. * * This method returns an array. The first item in the array is a boolean * indicating if login was successful. * * If login was successful, the second item in the array will contain the * current principal url/path of the logged in user. * * If login was not successful, the second item in the array will contain a * an array with strings. The strings are a list of reasons why login was * unsuccesful. For every auth backend there will be one reason, so usually * there's just one. * * @param RequestInterface $request * @param ResponseInterface $response * @return array */ function check(RequestInterface $request, ResponseInterface $response) { if (!$this->backends) { throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); } $reasons = []; foreach ($this->backends as $backend) { $result = $backend->check( $request, $response ); if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); } if ($result[0]) { $this->currentPrincipal = $result[1]; // Exit early return [true, $result[1]]; } $reasons[] = $result[1]; } return [false, $reasons]; } /** * This method sends authentication challenges to the user. * * This method will for example cause a HTTP Basic backend to set a * WWW-Authorization header, indicating to the client that it should * authenticate. * * @param RequestInterface $request * @param ResponseInterface $response * @return array */ function challenge(RequestInterface $request, ResponseInterface $response) { foreach ($this->backends as $backend) { $backend->challenge($request, $response); } } /** * List of reasons why login failed for the last login operation. * * @var string[]|null */ protected $loginFailedReasons; /** * Returns a list of reasons why login was unsuccessful. * * This method will return the login failed reasons for the last login * operation. One for each auth backend. * * This method returns null if the last authentication attempt was * successful, or if there was no authentication attempt yet. * * @return string[]|null */ function getLoginFailedReasons() { return $this->loginFailedReasons; } /** * Returns a bunch of meta-data about the plugin. * * Providing this information is optional, and is mainly displayed by the * Browser plugin. * * The description key in the returned array may contain html and will not * be sanitized. * * @return array */ function getPluginInfo() { return [ 'name' => $this->getPluginName(), 'description' => 'Generic authentication plugin', 'link' => 'http://sabre.io/dav/authentication/', ]; } }