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