1*a1a3b679SAndreas Boehler<?php 2*a1a3b679SAndreas Boehler 3*a1a3b679SAndreas Boehlernamespace Sabre\DAV\Auth\Backend; 4*a1a3b679SAndreas Boehler 5*a1a3b679SAndreas Boehleruse Sabre\HTTP; 6*a1a3b679SAndreas Boehleruse Sabre\DAV; 7*a1a3b679SAndreas Boehleruse Sabre\HTTP\RequestInterface; 8*a1a3b679SAndreas Boehleruse Sabre\HTTP\ResponseInterface; 9*a1a3b679SAndreas Boehler 10*a1a3b679SAndreas Boehler/** 11*a1a3b679SAndreas Boehler * HTTP Digest authentication backend class 12*a1a3b679SAndreas Boehler * 13*a1a3b679SAndreas Boehler * This class can be used by authentication objects wishing to use HTTP Digest 14*a1a3b679SAndreas Boehler * Most of the digest logic is handled, implementors just need to worry about 15*a1a3b679SAndreas Boehler * the getDigestHash method 16*a1a3b679SAndreas Boehler * 17*a1a3b679SAndreas Boehler * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). 18*a1a3b679SAndreas Boehler * @author Evert Pot (http://evertpot.com/) 19*a1a3b679SAndreas Boehler * @license http://sabre.io/license/ Modified BSD License 20*a1a3b679SAndreas Boehler */ 21*a1a3b679SAndreas Boehlerabstract class AbstractDigest implements BackendInterface { 22*a1a3b679SAndreas Boehler 23*a1a3b679SAndreas Boehler /** 24*a1a3b679SAndreas Boehler * Authentication Realm. 25*a1a3b679SAndreas Boehler * 26*a1a3b679SAndreas Boehler * The realm is often displayed by browser clients when showing the 27*a1a3b679SAndreas Boehler * authentication dialog. 28*a1a3b679SAndreas Boehler * 29*a1a3b679SAndreas Boehler * @var string 30*a1a3b679SAndreas Boehler */ 31*a1a3b679SAndreas Boehler protected $realm = 'SabreDAV'; 32*a1a3b679SAndreas Boehler 33*a1a3b679SAndreas Boehler /** 34*a1a3b679SAndreas Boehler * This is the prefix that will be used to generate principal urls. 35*a1a3b679SAndreas Boehler * 36*a1a3b679SAndreas Boehler * @var string 37*a1a3b679SAndreas Boehler */ 38*a1a3b679SAndreas Boehler protected $principalPrefix = 'principals/'; 39*a1a3b679SAndreas Boehler 40*a1a3b679SAndreas Boehler /** 41*a1a3b679SAndreas Boehler * Sets the authentication realm for this backend. 42*a1a3b679SAndreas Boehler * 43*a1a3b679SAndreas Boehler * Be aware that for Digest authentication, the realm influences the digest 44*a1a3b679SAndreas Boehler * hash. Choose the realm wisely, because if you change it later, all the 45*a1a3b679SAndreas Boehler * existing hashes will break and nobody can authenticate. 46*a1a3b679SAndreas Boehler * 47*a1a3b679SAndreas Boehler * @param string $realm 48*a1a3b679SAndreas Boehler * @return void 49*a1a3b679SAndreas Boehler */ 50*a1a3b679SAndreas Boehler function setRealm($realm) { 51*a1a3b679SAndreas Boehler 52*a1a3b679SAndreas Boehler $this->realm = $realm; 53*a1a3b679SAndreas Boehler 54*a1a3b679SAndreas Boehler } 55*a1a3b679SAndreas Boehler 56*a1a3b679SAndreas Boehler /** 57*a1a3b679SAndreas Boehler * Returns a users digest hash based on the username and realm. 58*a1a3b679SAndreas Boehler * 59*a1a3b679SAndreas Boehler * If the user was not known, null must be returned. 60*a1a3b679SAndreas Boehler * 61*a1a3b679SAndreas Boehler * @param string $realm 62*a1a3b679SAndreas Boehler * @param string $username 63*a1a3b679SAndreas Boehler * @return string|null 64*a1a3b679SAndreas Boehler */ 65*a1a3b679SAndreas Boehler abstract function getDigestHash($realm, $username); 66*a1a3b679SAndreas Boehler 67*a1a3b679SAndreas Boehler /** 68*a1a3b679SAndreas Boehler * When this method is called, the backend must check if authentication was 69*a1a3b679SAndreas Boehler * successful. 70*a1a3b679SAndreas Boehler * 71*a1a3b679SAndreas Boehler * The returned value must be one of the following 72*a1a3b679SAndreas Boehler * 73*a1a3b679SAndreas Boehler * [true, "principals/username"] 74*a1a3b679SAndreas Boehler * [false, "reason for failure"] 75*a1a3b679SAndreas Boehler * 76*a1a3b679SAndreas Boehler * If authentication was successful, it's expected that the authentication 77*a1a3b679SAndreas Boehler * backend returns a so-called principal url. 78*a1a3b679SAndreas Boehler * 79*a1a3b679SAndreas Boehler * Examples of a principal url: 80*a1a3b679SAndreas Boehler * 81*a1a3b679SAndreas Boehler * principals/admin 82*a1a3b679SAndreas Boehler * principals/user1 83*a1a3b679SAndreas Boehler * principals/users/joe 84*a1a3b679SAndreas Boehler * principals/uid/123457 85*a1a3b679SAndreas Boehler * 86*a1a3b679SAndreas Boehler * If you don't use WebDAV ACL (RFC3744) we recommend that you simply 87*a1a3b679SAndreas Boehler * return a string such as: 88*a1a3b679SAndreas Boehler * 89*a1a3b679SAndreas Boehler * principals/users/[username] 90*a1a3b679SAndreas Boehler * 91*a1a3b679SAndreas Boehler * @param RequestInterface $request 92*a1a3b679SAndreas Boehler * @param ResponseInterface $response 93*a1a3b679SAndreas Boehler * @return array 94*a1a3b679SAndreas Boehler */ 95*a1a3b679SAndreas Boehler function check(RequestInterface $request, ResponseInterface $response) { 96*a1a3b679SAndreas Boehler 97*a1a3b679SAndreas Boehler $digest = new HTTP\Auth\Digest( 98*a1a3b679SAndreas Boehler $this->realm, 99*a1a3b679SAndreas Boehler $request, 100*a1a3b679SAndreas Boehler $response 101*a1a3b679SAndreas Boehler ); 102*a1a3b679SAndreas Boehler $digest->init(); 103*a1a3b679SAndreas Boehler 104*a1a3b679SAndreas Boehler $username = $digest->getUsername(); 105*a1a3b679SAndreas Boehler 106*a1a3b679SAndreas Boehler // No username was given 107*a1a3b679SAndreas Boehler if (!$username) { 108*a1a3b679SAndreas Boehler return [false, "No 'Authorization: Digest' header found. Either the client didn't send one, or the server is mis-configured"]; 109*a1a3b679SAndreas Boehler } 110*a1a3b679SAndreas Boehler 111*a1a3b679SAndreas Boehler $hash = $this->getDigestHash($this->realm, $username); 112*a1a3b679SAndreas Boehler // If this was false, the user account didn't exist 113*a1a3b679SAndreas Boehler if ($hash === false || is_null($hash)) { 114*a1a3b679SAndreas Boehler return [false, "Username or password was incorrect"]; 115*a1a3b679SAndreas Boehler } 116*a1a3b679SAndreas Boehler if (!is_string($hash)) { 117*a1a3b679SAndreas Boehler throw new DAV\Exception('The returned value from getDigestHash must be a string or null'); 118*a1a3b679SAndreas Boehler } 119*a1a3b679SAndreas Boehler 120*a1a3b679SAndreas Boehler // If this was false, the password or part of the hash was incorrect. 121*a1a3b679SAndreas Boehler if (!$digest->validateA1($hash)) { 122*a1a3b679SAndreas Boehler return [false, "Username or password was incorrect"]; 123*a1a3b679SAndreas Boehler } 124*a1a3b679SAndreas Boehler 125*a1a3b679SAndreas Boehler return [true, $this->principalPrefix . $username]; 126*a1a3b679SAndreas Boehler 127*a1a3b679SAndreas Boehler } 128*a1a3b679SAndreas Boehler 129*a1a3b679SAndreas Boehler /** 130*a1a3b679SAndreas Boehler * This method is called when a user could not be authenticated, and 131*a1a3b679SAndreas Boehler * authentication was required for the current request. 132*a1a3b679SAndreas Boehler * 133*a1a3b679SAndreas Boehler * This gives you the opportunity to set authentication headers. The 401 134*a1a3b679SAndreas Boehler * status code will already be set. 135*a1a3b679SAndreas Boehler * 136*a1a3b679SAndreas Boehler * In this case of Basic Auth, this would for example mean that the 137*a1a3b679SAndreas Boehler * following header needs to be set: 138*a1a3b679SAndreas Boehler * 139*a1a3b679SAndreas Boehler * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); 140*a1a3b679SAndreas Boehler * 141*a1a3b679SAndreas Boehler * Keep in mind that in the case of multiple authentication backends, other 142*a1a3b679SAndreas Boehler * WWW-Authenticate headers may already have been set, and you'll want to 143*a1a3b679SAndreas Boehler * append your own WWW-Authenticate header instead of overwriting the 144*a1a3b679SAndreas Boehler * existing one. 145*a1a3b679SAndreas Boehler * 146*a1a3b679SAndreas Boehler * @param RequestInterface $request 147*a1a3b679SAndreas Boehler * @param ResponseInterface $response 148*a1a3b679SAndreas Boehler * @return void 149*a1a3b679SAndreas Boehler */ 150*a1a3b679SAndreas Boehler function challenge(RequestInterface $request, ResponseInterface $response) { 151*a1a3b679SAndreas Boehler 152*a1a3b679SAndreas Boehler $auth = new HTTP\Auth\Digest( 153*a1a3b679SAndreas Boehler $this->realm, 154*a1a3b679SAndreas Boehler $request, 155*a1a3b679SAndreas Boehler $response 156*a1a3b679SAndreas Boehler ); 157*a1a3b679SAndreas Boehler $auth->init(); 158*a1a3b679SAndreas Boehler $auth->requireLogin(); 159*a1a3b679SAndreas Boehler 160*a1a3b679SAndreas Boehler } 161*a1a3b679SAndreas Boehler 162*a1a3b679SAndreas Boehler} 163