xref: /plugin/davcal/vendor/sabre/dav/lib/DAV/Auth/Backend/AbstractDigest.php (revision a1a3b6794e0e143a4a8b51d3185ce2d339be61ab)
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