1*a1a3b679SAndreas Boehler<?php 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehlernamespace Sabre\DAV\Auth; 4*a1a3b679SAndreas Boehler 5*a1a3b679SAndreas Boehleruse Sabre\HTTP\RequestInterface; 6*a1a3b679SAndreas Boehleruse Sabre\HTTP\ResponseInterface; 7*a1a3b679SAndreas Boehleruse Sabre\HTTP\URLUtil; 8*a1a3b679SAndreas Boehleruse Sabre\DAV\Exception\NotAuthenticated; 9*a1a3b679SAndreas Boehleruse Sabre\DAV\Server; 10*a1a3b679SAndreas Boehleruse Sabre\DAV\ServerPlugin; 11*a1a3b679SAndreas Boehler 12*a1a3b679SAndreas Boehler/** 13*a1a3b679SAndreas Boehler * This plugin provides Authentication for a WebDAV server. 14*a1a3b679SAndreas Boehler * 15*a1a3b679SAndreas Boehler * It works by providing a Auth\Backend class. Several examples of these 16*a1a3b679SAndreas Boehler * classes can be found in the Backend directory. 17*a1a3b679SAndreas Boehler * 18*a1a3b679SAndreas Boehler * It's possible to provide more than one backend to this plugin. If more than 19*a1a3b679SAndreas Boehler * one backend was provided, each backend will attempt to authenticate. Only if 20*a1a3b679SAndreas Boehler * all backends fail, we throw a 401. 21*a1a3b679SAndreas Boehler * 22*a1a3b679SAndreas Boehler * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 23*a1a3b679SAndreas Boehler * @author Evert Pot (http://evertpot.com/) 24*a1a3b679SAndreas Boehler * @license http://sabre.io/license/ Modified BSD License 25*a1a3b679SAndreas Boehler */ 26*a1a3b679SAndreas Boehlerclass Plugin extends ServerPlugin { 27*a1a3b679SAndreas Boehler 28*a1a3b679SAndreas Boehler /** 29*a1a3b679SAndreas Boehler * authentication backends 30*a1a3b679SAndreas Boehler */ 31*a1a3b679SAndreas Boehler protected $backends; 32*a1a3b679SAndreas Boehler 33*a1a3b679SAndreas Boehler /** 34*a1a3b679SAndreas Boehler * The currently logged in principal. Will be `null` if nobody is currently 35*a1a3b679SAndreas Boehler * logged in. 36*a1a3b679SAndreas Boehler * 37*a1a3b679SAndreas Boehler * @var string|null 38*a1a3b679SAndreas Boehler */ 39*a1a3b679SAndreas Boehler protected $currentPrincipal; 40*a1a3b679SAndreas Boehler 41*a1a3b679SAndreas Boehler /** 42*a1a3b679SAndreas Boehler * Creates the authentication plugin 43*a1a3b679SAndreas Boehler * 44*a1a3b679SAndreas Boehler * @param Backend\BackendInterface $authBackend 45*a1a3b679SAndreas Boehler */ 46*a1a3b679SAndreas Boehler function __construct(Backend\BackendInterface $authBackend = null) { 47*a1a3b679SAndreas Boehler 48*a1a3b679SAndreas Boehler if (!is_null($authBackend)) { 49*a1a3b679SAndreas Boehler $this->addBackend($authBackend); 50*a1a3b679SAndreas Boehler } 51*a1a3b679SAndreas Boehler 52*a1a3b679SAndreas Boehler } 53*a1a3b679SAndreas Boehler 54*a1a3b679SAndreas Boehler /** 55*a1a3b679SAndreas Boehler * Adds an authentication backend to the plugin. 56*a1a3b679SAndreas Boehler * 57*a1a3b679SAndreas Boehler * @param Backend\BackendInterface $authBackend 58*a1a3b679SAndreas Boehler * @return void 59*a1a3b679SAndreas Boehler */ 60*a1a3b679SAndreas Boehler function addBackend(Backend\BackendInterface $authBackend) { 61*a1a3b679SAndreas Boehler 62*a1a3b679SAndreas Boehler $this->backends[] = $authBackend; 63*a1a3b679SAndreas Boehler 64*a1a3b679SAndreas Boehler } 65*a1a3b679SAndreas Boehler 66*a1a3b679SAndreas Boehler /** 67*a1a3b679SAndreas Boehler * Initializes the plugin. This function is automatically called by the server 68*a1a3b679SAndreas Boehler * 69*a1a3b679SAndreas Boehler * @param Server $server 70*a1a3b679SAndreas Boehler * @return void 71*a1a3b679SAndreas Boehler */ 72*a1a3b679SAndreas Boehler function initialize(Server $server) { 73*a1a3b679SAndreas Boehler 74*a1a3b679SAndreas Boehler $server->on('beforeMethod', [$this, 'beforeMethod'], 10); 75*a1a3b679SAndreas Boehler 76*a1a3b679SAndreas Boehler } 77*a1a3b679SAndreas Boehler 78*a1a3b679SAndreas Boehler /** 79*a1a3b679SAndreas Boehler * Returns a plugin name. 80*a1a3b679SAndreas Boehler * 81*a1a3b679SAndreas Boehler * Using this name other plugins will be able to access other plugins 82*a1a3b679SAndreas Boehler * using DAV\Server::getPlugin 83*a1a3b679SAndreas Boehler * 84*a1a3b679SAndreas Boehler * @return string 85*a1a3b679SAndreas Boehler */ 86*a1a3b679SAndreas Boehler function getPluginName() { 87*a1a3b679SAndreas Boehler 88*a1a3b679SAndreas Boehler return 'auth'; 89*a1a3b679SAndreas Boehler 90*a1a3b679SAndreas Boehler } 91*a1a3b679SAndreas Boehler 92*a1a3b679SAndreas Boehler /** 93*a1a3b679SAndreas Boehler * Returns the currently logged-in principal. 94*a1a3b679SAndreas Boehler * 95*a1a3b679SAndreas Boehler * This will return a string such as: 96*a1a3b679SAndreas Boehler * 97*a1a3b679SAndreas Boehler * principals/username 98*a1a3b679SAndreas Boehler * principals/users/username 99*a1a3b679SAndreas Boehler * 100*a1a3b679SAndreas Boehler * This method will return null if nobody is logged in. 101*a1a3b679SAndreas Boehler * 102*a1a3b679SAndreas Boehler * @return string|null 103*a1a3b679SAndreas Boehler */ 104*a1a3b679SAndreas Boehler function getCurrentPrincipal() { 105*a1a3b679SAndreas Boehler 106*a1a3b679SAndreas Boehler return $this->currentPrincipal; 107*a1a3b679SAndreas Boehler 108*a1a3b679SAndreas Boehler } 109*a1a3b679SAndreas Boehler 110*a1a3b679SAndreas Boehler /** 111*a1a3b679SAndreas Boehler * Returns the current username. 112*a1a3b679SAndreas Boehler * 113*a1a3b679SAndreas Boehler * This method is deprecated and is only kept for backwards compatibility 114*a1a3b679SAndreas Boehler * purposes. Please switch to getCurrentPrincipal(). 115*a1a3b679SAndreas Boehler * 116*a1a3b679SAndreas Boehler * @deprecated Will be removed in a future version! 117*a1a3b679SAndreas Boehler * @return string|null 118*a1a3b679SAndreas Boehler */ 119*a1a3b679SAndreas Boehler function getCurrentUser() { 120*a1a3b679SAndreas Boehler 121*a1a3b679SAndreas Boehler // We just do a 'basename' on the principal to give back a sane value 122*a1a3b679SAndreas Boehler // here. 123*a1a3b679SAndreas Boehler list(, $userName) = URLUtil::splitPath( 124*a1a3b679SAndreas Boehler $this->getCurrentPrincipal() 125*a1a3b679SAndreas Boehler ); 126*a1a3b679SAndreas Boehler 127*a1a3b679SAndreas Boehler return $userName; 128*a1a3b679SAndreas Boehler 129*a1a3b679SAndreas Boehler } 130*a1a3b679SAndreas Boehler 131*a1a3b679SAndreas Boehler /** 132*a1a3b679SAndreas Boehler * This method is called before any HTTP method and forces users to be authenticated 133*a1a3b679SAndreas Boehler * 134*a1a3b679SAndreas Boehler * @param RequestInterface $request 135*a1a3b679SAndreas Boehler * @param ResponseInterface $response 136*a1a3b679SAndreas Boehler * @return bool 137*a1a3b679SAndreas Boehler */ 138*a1a3b679SAndreas Boehler function beforeMethod(RequestInterface $request, ResponseInterface $response) { 139*a1a3b679SAndreas Boehler 140*a1a3b679SAndreas Boehler if ($this->currentPrincipal) { 141*a1a3b679SAndreas Boehler 142*a1a3b679SAndreas Boehler // We already have authentication information. This means that the 143*a1a3b679SAndreas Boehler // event has already fired earlier, and is now likely fired for a 144*a1a3b679SAndreas Boehler // sub-request. 145*a1a3b679SAndreas Boehler // 146*a1a3b679SAndreas Boehler // We don't want to authenticate users twice, so we simply don't do 147*a1a3b679SAndreas Boehler // anything here. See Issue #700 for additional reasoning. 148*a1a3b679SAndreas Boehler // 149*a1a3b679SAndreas Boehler // This is not a perfect solution, but will be fixed once the 150*a1a3b679SAndreas Boehler // "currently authenticated principal" is information that's not 151*a1a3b679SAndreas Boehler // not associated with the plugin, but rather per-request. 152*a1a3b679SAndreas Boehler // 153*a1a3b679SAndreas Boehler // See issue #580 for more information about that. 154*a1a3b679SAndreas Boehler return; 155*a1a3b679SAndreas Boehler 156*a1a3b679SAndreas Boehler } 157*a1a3b679SAndreas Boehler if (!$this->backends) { 158*a1a3b679SAndreas Boehler throw new \Sabre\DAV\Exception('No authentication backends were configured on this server.'); 159*a1a3b679SAndreas Boehler } 160*a1a3b679SAndreas Boehler $reasons = []; 161*a1a3b679SAndreas Boehler foreach ($this->backends as $backend) { 162*a1a3b679SAndreas Boehler 163*a1a3b679SAndreas Boehler $result = $backend->check( 164*a1a3b679SAndreas Boehler $request, 165*a1a3b679SAndreas Boehler $response 166*a1a3b679SAndreas Boehler ); 167*a1a3b679SAndreas Boehler 168*a1a3b679SAndreas Boehler if (!is_array($result) || count($result) !== 2 || !is_bool($result[0]) || !is_string($result[1])) { 169*a1a3b679SAndreas Boehler throw new \Sabre\DAV\Exception('The authentication backend did not return a correct value from the check() method.'); 170*a1a3b679SAndreas Boehler } 171*a1a3b679SAndreas Boehler 172*a1a3b679SAndreas Boehler if ($result[0]) { 173*a1a3b679SAndreas Boehler $this->currentPrincipal = $result[1]; 174*a1a3b679SAndreas Boehler // Exit early 175*a1a3b679SAndreas Boehler return; 176*a1a3b679SAndreas Boehler } 177*a1a3b679SAndreas Boehler $reasons[] = $result[1]; 178*a1a3b679SAndreas Boehler 179*a1a3b679SAndreas Boehler } 180*a1a3b679SAndreas Boehler 181*a1a3b679SAndreas Boehler // If we got here, it means that no authentication backend was 182*a1a3b679SAndreas Boehler // successful in authenticating the user. 183*a1a3b679SAndreas Boehler $this->currentPrincipal = null; 184*a1a3b679SAndreas Boehler 185*a1a3b679SAndreas Boehler foreach ($this->backends as $backend) { 186*a1a3b679SAndreas Boehler $backend->challenge($request, $response); 187*a1a3b679SAndreas Boehler } 188*a1a3b679SAndreas Boehler throw new NotAuthenticated(implode(', ', $reasons)); 189*a1a3b679SAndreas Boehler 190*a1a3b679SAndreas Boehler } 191*a1a3b679SAndreas Boehler 192*a1a3b679SAndreas Boehler /** 193*a1a3b679SAndreas Boehler * Returns a bunch of meta-data about the plugin. 194*a1a3b679SAndreas Boehler * 195*a1a3b679SAndreas Boehler * Providing this information is optional, and is mainly displayed by the 196*a1a3b679SAndreas Boehler * Browser plugin. 197*a1a3b679SAndreas Boehler * 198*a1a3b679SAndreas Boehler * The description key in the returned array may contain html and will not 199*a1a3b679SAndreas Boehler * be sanitized. 200*a1a3b679SAndreas Boehler * 201*a1a3b679SAndreas Boehler * @return array 202*a1a3b679SAndreas Boehler */ 203*a1a3b679SAndreas Boehler function getPluginInfo() { 204*a1a3b679SAndreas Boehler 205*a1a3b679SAndreas Boehler return [ 206*a1a3b679SAndreas Boehler 'name' => $this->getPluginName(), 207*a1a3b679SAndreas Boehler 'description' => 'Generic authentication plugin', 208*a1a3b679SAndreas Boehler 'link' => 'http://sabre.io/dav/authentication/', 209*a1a3b679SAndreas Boehler ]; 210*a1a3b679SAndreas Boehler 211*a1a3b679SAndreas Boehler } 212*a1a3b679SAndreas Boehler 213*a1a3b679SAndreas Boehler} 214