Issues (58)

src/BearerAuthenticator.php (7 issues)

1
<?php
2
3
/**
4
 * AppserverIo\Authenticator\BearerAuthenticator
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/authenticator
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Authenticator;
22
23
use AppserverIo\Lang\String;
24
use AppserverIo\Psr\Auth\RealmInterface;
25
use AppserverIo\Psr\HttpMessage\Protocol;
26
use AppserverIo\Psr\Security\PrincipalInterface;
27
use AppserverIo\Psr\Servlet\ServletException;
28
use AppserverIo\Psr\Servlet\Utils\RequestHandlerKeys;
29
use AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface;
30
use AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface;
31
32
/**
33
 * A bearer token based authenticator implementation.
34
 *
35
 * @author    Tim Wagner <[email protected]>
36
 * @copyright 2016 TechDivision GmbH <[email protected]>
37
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
38
 * @link      https://github.com/appserver-io/authenticator
39
 * @link      http://www.appserver.io
40
 */
41
class BearerAuthenticator extends AbstractAuthenticator
42
{
43
44
    /**
45
     * Defines the auth type which should match the client request type definition
46
     *
47
     * @var string AUTH_TYPE
48
     */
49
    const AUTH_TYPE = 'Bearer';
50
51
    /**
52
     * Returns the parsed password.
53
     *
54
     * @return \AppserverIo\Lang\String The password
55
     */
56
    public function getPassword()
57
    {
58
        return new String();
59
    }
60
61
    /**
62
     * Return's the array with the login credentials.
63
     *
64
     * @return \AppserverIo\Lang\String[] The array with the login credentials
65
     */
66
    protected function getCredentials()
67
    {
68
        return array($this->getUsername(), $this->getPassword());
0 ignored issues
show
Bug Best Practice introduced by
The expression return array($this->getU..., $this->getPassword()) returns an array which contains values of type string which are incompatible with the documented value type AppserverIo\Lang\String.
Loading history...
69
    }
70
71
    /**
72
     * Try to authenticate the user making this request, based on the specified login configuration.
73
     *
74
     * Return TRUE if any specified constraint has been satisfied, or FALSE if we have created a response
75
     * challenge already.
76
     *
77
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
78
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
79
     *
80
     * @return boolean TRUE if authentication has already been processed on a request before, else FALSE
81
     * @throws \AppserverIo\Http\Authentication\AuthenticationException Is thrown if the request can't be authenticated
82
     */
83
    public function authenticate(HttpServletRequestInterface $servletRequest, HttpServletResponseInterface $servletResponse)
84
    {
85
86
        // invoke the onCredentials callback to load the credentials from the request
87
        $this->onCredentials($servletRequest, $servletResponse);
88
89
        // load the realm to authenticate this request for
90
        /** @var AppserverIo\Appserver\ServletEngine\Security\RealmInterface $realm */
91
        $realm = $this->getAuthenticationManager()->getRealm($this->getRealmName());
92
93
        // authenticate the request and initialize the user principal
94
        $userPrincipal = call_user_func_array(array($realm, 'authenticate'), $this->getCredentials());
95
96
        // query whether or not the realm returned an authenticated user principal
97
        if ($userPrincipal == null) {
98
            // invoke the onFailure callback and forward the user to the error page
99
            $this->onFailure($realm, $servletRequest, $servletResponse);
100
            return false;
101
        }
102
103
        // invoke the onSuccess callback and redirect the user to the original page
104
        $this->onSuccess($userPrincipal, $servletRequest, $servletResponse);
105
        return false;
106
    }
107
108
    /**
109
     * Will be invoked to load the credentials from the request.
110
     *
111
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
112
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
113
     *
114
     * @return void
115
     */
116
    protected function onCredentials(
117
        HttpServletRequestInterface $servletRequest,
118
        HttpServletResponseInterface $servletResponse
119
    ) {
120
121
        // try to load the access token from the request instead
122
        if ($servletRequest->hasHeader(Protocol::HEADER_AUTHORIZATION)) {
123
            // extract the access token from the authorization header
124
            sscanf($servletRequest->getHeader(Protocol::HEADER_AUTHORIZATION), 'Bearer %s', $accessToken);
0 ignored issues
show
It seems like $servletRequest->getHead...::HEADER_AUTHORIZATION) can also be of type null; however, parameter $string of sscanf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
            sscanf(/** @scrutinizer ignore-type */ $servletRequest->getHeader(Protocol::HEADER_AUTHORIZATION), 'Bearer %s', $accessToken);
Loading history...
125
            $this->username = new String($accessToken);
126
        }
127
    }
128
129
    /**
130
     * Will be invoked when login fails for some reasons.
131
     *
132
     * @param \AppserverIo\Appserver\ServletEngine\Security\RealmInterface $realm           The realm instance containing the exception stack
0 ignored issues
show
The type AppserverIo\Appserver\Se...Security\RealmInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
133
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface    $servletRequest  The servlet request instance
134
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface   $servletResponse The servlet response instance
135
     *
136
     * @return void
137
     */
138
    protected function onFailure(
139
        RealmInterface $realm,
140
        HttpServletRequestInterface $servletRequest,
141
        HttpServletResponseInterface $servletResponse
142
    ) {
143
        $this->forwardToErrorPage($servletRequest, $servletResponse);
144
    }
145
146
    /**
147
     * Will be invoked on a successfull login.
148
     *
149
     * @param \AppserverIo\Psr\Security\PrincipalInterface               $userPrincipal   The user principal logged into the system
150
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
151
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
152
     *
153
     * @return void
154
     */
155
    protected function onSuccess(
156
        PrincipalInterface $userPrincipal,
157
        HttpServletRequestInterface $servletRequest,
158
        HttpServletResponseInterface $servletResponse
159
    ) {
160
161
        // add the user principal and the authentication type to the request
162
        $this->register($servletRequest, $servletResponse, $userPrincipal);
163
    }
164
165
    /**
166
     * Register's the user principal and the authenticytion in the request and session.
167
     *
168
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
169
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
170
     * @param \AppserverIo\Psr\Security\PrincipalInterface               $userPrincipal   The actual user principal
171
     *
172
     * @return void
173
     */
174
    protected function register(
175
        HttpServletRequestInterface $servletRequest,
176
        HttpServletResponseInterface $servletResponse,
177
        PrincipalInterface $userPrincipal
178
    ) {
179
180
        // add the user principal and the authentication type to the request
181
        $servletRequest->setUserPrincipal($userPrincipal);
0 ignored issues
show
The method setUserPrincipal() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Authenticato...ServletRequestInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
        $servletRequest->/** @scrutinizer ignore-call */ 
182
                         setUserPrincipal($userPrincipal);
Loading history...
182
        $servletRequest->setAuthType($this->getAuthType());
0 ignored issues
show
The method setAuthType() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

182
        $servletRequest->/** @scrutinizer ignore-call */ 
183
                         setAuthType($this->getAuthType());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
183
    }
184
185
    /**
186
     * Forward's the request to the configured login page.
187
     *
188
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
189
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
190
     *
191
     * @return void
192
     */
193
    protected function forwardToLoginPage(
194
        HttpServletRequestInterface $servletRequest,
195
        HttpServletResponseInterface $servletResponse
196
    ) {
197
        $servletRequest->setDispatched(true);
198
        $servletResponse->setHeaders($this->getDefaultHeaders());
199
        $servletResponse->appendBodyStream($this->serialize(array('error' => 'Use SSO server to aquire a valid access token')));
200
        $servletResponse->setStatusCode(500);
201
    }
202
203
    /**
204
     * Forward's the request to the configured error page.
205
     *
206
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The servlet request instance
207
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The servlet response instance
208
     *
209
     * @return void
210
     */
211
    protected function forwardToErrorPage(
212
        HttpServletRequestInterface $servletRequest,
213
        HttpServletResponseInterface $servletResponse
214
    ) {
215
        $servletRequest->setDispatched(true);
216
        $servletResponse->setHeaders($this->getDefaultHeaders());
217
        $servletResponse->appendBodyStream($this->serialize(array('error' => 'You need an valid access token to use the API')));
218
        $servletResponse->setStatusCode(401);
219
    }
220
221
    /**
222
     * Return's the default headers to set.
223
     *
224
     * @return string[] The array with the headers
225
     */
226
    protected function getDefaultHeaders()
227
    {
228
        return array(Protocol::HEADER_CONTENT_TYPE => 'application/json');
0 ignored issues
show
Bug Best Practice introduced by
The expression return array(AppserverIo... => 'application/json') returns the type array<string,string> which is incompatible with the documented return type AppserverIo\Lang\String[].
Loading history...
229
    }
230
231
    /**
232
     * Serialize's the passed value an return's it.
233
     *
234
     * @param mixed $value The value that has to be serialized
235
     *
236
     * @return string The serialized value
237
     */
238
    protected function serialize($value)
239
    {
240
        return json_encode($value);
0 ignored issues
show
Bug Best Practice introduced by
The expression return json_encode($value) returns the type string which is incompatible with the documented return type AppserverIo\Lang\String.
Loading history...
241
    }
242
243
    /**
244
     * Tries the login the passed username/password combination for the login configuration.
245
     *
246
     * @param \AppserverIo\Lang\String                                  $username       The username used to login
247
     * @param \AppserverIo\Lang\String                                  $password       The password used to authenticate the user
248
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface $servletRequest The servlet request instance
249
     *
250
     * @return \AppserverIo\Psr\Security\PrincipalInterface The authenticated user principal
251
     */
252
    public function login(
253
        String $username,
254
        String $password,
255
        HttpServletRequestInterface $servletRequest
256
    ) {
257
258
        // load the realm to authenticate this request for
259
        /** @var AppserverIo\Appserver\ServletEngine\Security\RealmInterface $realm */
260
        $realm = $this->getAuthenticationManager()->getRealm($this->getRealmName());
261
262
        // authenticate the request and initialize the user principal
263
        $userPrincipal = call_user_func_array(array($realm, 'authenticate'), array($username, $password));
264
265
        // query whether or not we can authenticate the user
266
        if ($userPrincipal == null) {
267
            throw new ServletException(sprintf('Can\'t authenticate user %s', $username));
268
        }
269
270
        // add the user principal and the authentication type to the request
271
        $servletRequest->setUserPrincipal($userPrincipal);
272
        $servletRequest->setAuthType($this->getAuthType());
273
274
        // return's the user principal
275
        return $userPrincipal;
276
    }
277
278
    /**
279
     * Logout the actual user from the session.
280
     *
281
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface $servletRequest The servlet request instance
282
     *
283
     * @return void
284
     */
285
    public function logout(HttpServletRequestInterface $servletRequest)
286
    {
287
    }
288
}
289