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