This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * OpauthController |
||
5 | * Wraps around Opauth for handling callbacks. |
||
6 | * The SS equivalent of "index.php" and "callback.php" in the Opauth package. |
||
7 | * @author Will Morgan <@willmorgan> |
||
8 | * @author Dan Hensby <@dhensby> |
||
9 | * @copyright Copyright (c) 2013, Better Brief LLP |
||
10 | */ |
||
11 | class OpauthController extends ContentController { |
||
0 ignored issues
–
show
|
|||
12 | |||
13 | private static |
||
14 | $allowed_actions = array( |
||
0 ignored issues
–
show
The visibility should be declared for property
$allowed_actions .
The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using class A {
var $property;
}
the property is implicitly global. To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2. ![]() |
|||
15 | 'index', |
||
16 | 'finished', |
||
17 | 'profilecompletion', |
||
18 | 'RegisterForm', |
||
19 | ), |
||
20 | $url_handlers = array( |
||
0 ignored issues
–
show
|
|||
21 | 'finished' => 'finished', |
||
22 | ); |
||
23 | |||
24 | /** |
||
25 | * Bitwise indicators to extensions what sort of action is happening |
||
26 | */ |
||
27 | const |
||
28 | /** |
||
29 | * LOGIN = already a user with an OAuth ID |
||
30 | */ |
||
31 | AUTH_FLAG_LOGIN = 2, |
||
32 | /** |
||
33 | * LINK = already a user, linking a new OAuth ID |
||
34 | */ |
||
35 | AUTH_FLAG_LINK = 4, |
||
36 | /** |
||
37 | * REGISTER = new user, linking OAuth ID |
||
38 | */ |
||
39 | AUTH_FLAG_REGISTER = 8; |
||
40 | |||
41 | protected |
||
42 | $registerForm; |
||
0 ignored issues
–
show
The visibility should be declared for property
$registerForm .
The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using class A {
var $property;
}
the property is implicitly global. To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2. ![]() |
|||
43 | |||
44 | /** |
||
45 | * Fake a Page_Controller by using that class as a failover |
||
46 | */ |
||
47 | public function __construct($dataRecord = null) { |
||
48 | if(class_exists('Page_Controller')) { |
||
49 | $dataRecord = new Page_Controller($dataRecord); |
||
50 | } |
||
51 | parent::__construct($dataRecord); |
||
52 | } |
||
53 | |||
54 | /** |
||
55 | * This function only catches the request to pass it straight on. |
||
56 | * Opauth uses the last segment of the URL to identify the auth method. |
||
57 | * In _routes.yml we enforce a $Strategy request parameter to enforce this. |
||
58 | * Equivalent to "index.php" in the Opauth package. |
||
59 | * @todo: Validate the strategy works before delegating to Opauth. |
||
60 | */ |
||
61 | public function index(SS_HTTPRequest $request) { |
||
62 | |||
63 | $strategy = $request->param('Strategy'); |
||
64 | $method = $request->param('StrategyMethod'); |
||
65 | |||
66 | if(!isset($strategy)) { |
||
67 | return Security::permissionFailure($this); |
||
68 | } |
||
69 | |||
70 | // If there is no method then we redirect (not a callback) |
||
71 | if(!isset($method)) { |
||
72 | // Redirects: |
||
73 | OpauthAuthenticator::opauth(true); |
||
74 | } |
||
75 | else { |
||
76 | return $this->oauthCallback($request); |
||
77 | } |
||
78 | } |
||
79 | |||
80 | /** |
||
81 | * This is executed when the Oauth provider redirects back to us |
||
82 | * Opauth handles everything sent back in this request. |
||
83 | */ |
||
84 | protected function oauthCallback(SS_HTTPRequest $request) { |
||
85 | |||
86 | // Set up and run opauth with the correct params from the strategy: |
||
87 | OpauthAuthenticator::opauth(true, array( |
||
88 | 'strategy' => $request->param('Strategy'), |
||
89 | 'action' => $request->param('StrategyMethod'), |
||
90 | )); |
||
91 | |||
92 | } |
||
93 | |||
94 | /** |
||
95 | * Equivalent to "callback.php" in the Opauth package. |
||
96 | * If there is a problem with the response, we throw an HTTP error. |
||
97 | * When done validating, we return back to the Authenticator continue auth. |
||
98 | * @throws SS_HTTPResponse_Exception if any validation errors |
||
99 | */ |
||
100 | public function finished(SS_HTTPRequest $request) { |
||
101 | |||
102 | $opauth = OpauthAuthenticator::opauth(false); |
||
103 | |||
104 | $response = $this->getOpauthResponse(); |
||
105 | |||
106 | if (!$response) { |
||
0 ignored issues
–
show
The expression
$response of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
107 | $response = array(); |
||
108 | } |
||
109 | // Clear the response as it is only to be read once (if Session) |
||
110 | Session::clear('opauth'); |
||
111 | |||
112 | // Handle all Opauth validation in this handy function |
||
113 | try { |
||
114 | $this->validateOpauthResponse($opauth, $response); |
||
115 | } |
||
116 | catch(OpauthValidationException $e) { |
||
117 | return $this->handleOpauthException($e); |
||
118 | } |
||
119 | |||
120 | $identity = OpauthIdentity::factory($response); |
||
121 | |||
122 | $member = $identity->findOrCreateMember(); |
||
123 | |||
124 | // If the member exists, associate it with the identity and log in |
||
125 | if($member->isInDB() && $member->validate()->valid()) { |
||
126 | if(!$identity->exists()) { |
||
127 | $identity->write(); |
||
128 | $flag = self::AUTH_FLAG_LINK; |
||
129 | } |
||
130 | else { |
||
131 | $flag = self::AUTH_FLAG_LOGIN; |
||
132 | } |
||
133 | |||
134 | Session::set('OpauthIdentityID', $identity->ID); |
||
135 | } |
||
136 | else { |
||
137 | |||
138 | $flag = self::AUTH_FLAG_REGISTER; |
||
139 | |||
140 | // Write the identity |
||
141 | $identity->write(); |
||
142 | |||
143 | // Keep a note of the identity ID |
||
144 | Session::set('OpauthIdentityID', $identity->ID); |
||
145 | |||
146 | // Even if written, check validation - we might not have full fields |
||
147 | $validationResult = $member->validate(); |
||
148 | if(!$validationResult->valid()) { |
||
149 | // Set up the register form before it's output |
||
150 | $regForm = $this->RegisterForm(); |
||
151 | $regForm->loadDataFrom($member); |
||
152 | $regForm->setSessionData($member); |
||
153 | $regForm->validate(); |
||
154 | return $this->redirect($this->Link('profilecompletion')); |
||
155 | } |
||
156 | else { |
||
157 | $member->extend('onBeforeOpauthRegister'); |
||
158 | $member->write(); |
||
159 | $identity->MemberID = $member->ID; |
||
0 ignored issues
–
show
The property
MemberID does not exist on object<OpauthIdentity> . Since you implemented __set , maybe consider adding a @property annotation.
Since your code implements the magic setter <?php
/**
* @property int $x
* @property int $y
* @property string $text
*/
class MyLabel
{
private $properties;
private $allowedProperties = array('x', 'y', 'text');
public function __get($name)
{
if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
return $properties[$name];
} else {
return null;
}
}
public function __set($name, $value)
{
if (in_array($name, $this->allowedProperties)) {
$properties[$name] = $value;
} else {
throw new \LogicException("Property $name is not defined.");
}
}
}
Since the property has write access only, you can use the @property-write annotation instead. Of course, you may also just have mistyped another name, in which case you should fix the error. See also the PhpDoc documentation for @property. ![]() |
|||
160 | $identity->write(); |
||
161 | } |
||
162 | } |
||
163 | return $this->loginAndRedirect($member, $identity, $flag); |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param Member |
||
168 | * @param OpauthIdentity |
||
169 | * @param int $mode One or more AUTH_FLAGs. |
||
170 | */ |
||
171 | protected function loginAndRedirect(Member $member, OpauthIdentity $identity, $mode) { |
||
172 | // Back up the BackURL as Member::logIn regenerates the session |
||
173 | $backURL = Session::get('BackURL'); |
||
174 | |||
175 | // Check if we can log in: |
||
176 | $canLogIn = $member->canLogIn(); |
||
177 | |||
178 | if(!$canLogIn->valid()) { |
||
179 | $extendedURLs = $this->extend('getCantLoginBackURL', $member, $identity, $canLogIn, $mode); |
||
180 | if(count($extendedURLs)) { |
||
181 | $redirectURL = array_pop($extendedURLs); |
||
182 | $this->redirect($redirectURL, 302); |
||
183 | return; |
||
184 | } |
||
185 | Security::permissionFailure($this, $canLogIn->message()); |
||
186 | return; |
||
187 | } |
||
188 | |||
189 | // Decide where to go afterwards... |
||
190 | if(!empty($backURL)) { |
||
191 | $redirectURL = $backURL; |
||
192 | } |
||
193 | else { |
||
194 | $redirectURL = Security::config()->default_login_dest; |
||
195 | } |
||
196 | |||
197 | $extendedURLs = $this->extend('getSuccessBackURL', $member, $identity, $redirectURL, $mode); |
||
198 | |||
199 | if(count($extendedURLs)) { |
||
200 | $redirectURL = array_pop($extendedURLs); |
||
201 | } |
||
202 | |||
203 | $member->logIn(); |
||
204 | |||
205 | // Clear any identity ID |
||
206 | Session::clear('OpauthIdentityID'); |
||
207 | |||
208 | // Clear the BackURL |
||
209 | Session::clear('BackURL'); |
||
210 | |||
211 | return $this->redirect($redirectURL); |
||
212 | } |
||
213 | |||
214 | public function profilecompletion(SS_HTTPRequest $request = null) { |
||
215 | if(!Session::get('OpauthIdentityID')) { |
||
216 | Security::permissionFailure($this); |
||
217 | } |
||
218 | // Redirect to complete register step by adding in extra info |
||
219 | return $this->renderWith(array( |
||
220 | 'OpauthController_profilecompletion', |
||
221 | 'Security_profilecompletion', |
||
222 | 'Page', |
||
223 | ) |
||
224 | ); |
||
225 | } |
||
226 | |||
227 | public function RegisterForm(SS_HTTPRequest $request = null, Member $member = null, $result = null) { |
||
228 | if(!isset($this->registerForm)) { |
||
229 | $form = Injector::inst()->create('OpauthRegisterForm', $this, 'RegisterForm', $result); |
||
230 | $form->populateFromSources($request, $member, $result); |
||
231 | // Set manually the form action due to how routing works |
||
232 | $form->setFormAction(Controller::join_links( |
||
233 | self::config()->opauth_path, |
||
234 | 'RegisterForm' |
||
235 | )); |
||
236 | $this->registerForm = $form; |
||
237 | } |
||
238 | else { |
||
239 | $this->registerForm->populateFromSources($request, $member, $result); |
||
240 | } |
||
241 | return $this->registerForm; |
||
242 | } |
||
243 | |||
244 | public function doCompleteRegister($data, $form, $request) { |
||
245 | $member = new Member(); |
||
246 | $form->saveInto($member); |
||
247 | $identityID = Session::get('OpauthIdentityID'); |
||
248 | $identity = DataObject::get_by_id('OpauthIdentity', $identityID); |
||
249 | $validationResult = $member->validate(); |
||
250 | $existing = Member::get()->filter('Email', $member->Email)->first(); |
||
251 | $emailCollision = $existing && $existing->exists(); |
||
252 | // If not valid then we have to manually transpose errors to the form |
||
253 | if(!$validationResult->valid() || $emailCollision) { |
||
254 | $errors = $validationResult->messageList(); |
||
255 | $form->setRequiredFields($errors); |
||
256 | // Mandatory check on the email address |
||
257 | if($emailCollision) { |
||
258 | $form->addErrorMessage('Email', _t( |
||
259 | 'OpauthRegisterForm.ERROREMAILTAKEN', |
||
260 | 'It looks like this email has already been used' |
||
261 | ), 'required'); |
||
262 | } |
||
263 | return $this->redirect('profilecompletion'); |
||
264 | } |
||
265 | // If valid then write and redirect |
||
266 | else { |
||
267 | $member->extend('onBeforeOpauthRegister'); |
||
268 | $member->write(); |
||
269 | $identity->MemberID = $member->ID; |
||
270 | $identity->write(); |
||
271 | return $this->loginAndRedirect($member, $identity, self::AUTH_FLAG_REGISTER); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Returns the response from the Oauth callback. |
||
277 | * @throws InvalidArugmentException |
||
278 | * @return array The response |
||
279 | */ |
||
280 | protected function getOpauthResponse() { |
||
281 | $config = OpauthAuthenticator::get_opauth_config(); |
||
282 | $transportMethod = $config['callback_transport']; |
||
283 | switch($transportMethod) { |
||
284 | case 'session': |
||
285 | return $this->getResponseFromSession(); |
||
286 | case 'get': |
||
287 | case 'post': |
||
288 | return $this->getResponseFromRequest($transportMethod); |
||
289 | default: |
||
290 | throw new InvalidArgumentException('Invalid transport method: ' . $transportMethod); |
||
291 | } |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Validates the Oauth response for Opauth. |
||
296 | * @throws InvalidArgumentException |
||
297 | */ |
||
298 | protected function validateOpauthResponse($opauth, $response) { |
||
299 | if(!empty($response['error'])) { |
||
300 | throw new OpauthValidationException('Oauth provider error', 1, $response['error']); |
||
301 | } |
||
302 | |||
303 | // Required components within the response |
||
304 | $this->requireResponseComponents( |
||
305 | array('auth', 'timestamp', 'signature'), |
||
306 | $response |
||
307 | ); |
||
308 | |||
309 | // More required components within the auth section... |
||
310 | $this->requireResponseComponents( |
||
311 | array('provider', 'uid'), |
||
312 | $response['auth'] |
||
313 | ); |
||
314 | |||
315 | $invalidReason = ''; |
||
316 | |||
317 | /** |
||
318 | * @todo: improve this signature check. it's a bit weak. |
||
319 | */ |
||
320 | if(!$opauth->validate( |
||
321 | sha1(print_r($response['auth'], true)), |
||
322 | $response['timestamp'], |
||
323 | $response['signature'], |
||
324 | $invalidReason |
||
325 | )) { |
||
326 | throw new OpauthValidationException('Invalid auth response', 3, $invalidReason); |
||
327 | } |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Shorthand for quickly finding missing components and complaining about it |
||
332 | * @throws InvalidArgumentException |
||
333 | */ |
||
334 | protected function requireResponseComponents(array $components, $response) { |
||
335 | foreach($components as $component) { |
||
336 | if(empty($response[$component])) { |
||
337 | throw new OpauthValidationException('Required component missing', 2, $component); |
||
338 | } |
||
339 | } |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * @return array Opauth response from session |
||
344 | */ |
||
345 | protected function getResponseFromSession() { |
||
346 | return Session::get('opauth'); |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * @param OpauthValidationException $e |
||
351 | */ |
||
352 | protected function handleOpauthException(OpauthValidationException $e) { |
||
353 | $data = $e->getData(); |
||
354 | $loginFormName = 'OpauthLoginForm_LoginForm'; |
||
355 | $message = ''; |
||
356 | switch($e->getCode()) { |
||
357 | case 1: // provider error |
||
358 | $message = _t( |
||
359 | 'OpauthLoginForm.OAUTHFAILURE', |
||
360 | 'There was a problem logging in with {provider}.', |
||
361 | array( |
||
0 ignored issues
–
show
array('provider' => $data['provider']) is of type array<string,?,{"provider":"?"}> , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
362 | 'provider' => $data['provider'], |
||
363 | ) |
||
364 | ); |
||
365 | break; |
||
366 | case 2: // validation error |
||
367 | case 3: // invalid auth response |
||
368 | $message = _t( |
||
369 | 'OpauthLoginForm.RESPONSEVALIDATIONFAILURE', |
||
370 | 'There was a problem logging in - {message}', |
||
371 | array( |
||
0 ignored issues
–
show
array('message' => $e->getMessage()) is of type array<string,string,{"message":"string"}> , but the function expects a string .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
372 | 'message' => $e->getMessage(), |
||
373 | ) |
||
374 | ); |
||
375 | break; |
||
376 | } |
||
377 | // Set form message, redirect to login with permission failure |
||
378 | Form::messageForForm($loginFormName, $message, 'bad'); |
||
379 | // always redirect to login |
||
380 | Security::permissionFailure($this, $message); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Looks at $method (GET, POST, PUT etc) for the response. |
||
385 | * @return array Opauth response |
||
386 | */ |
||
387 | protected function getResponseFromRequest($method) { |
||
388 | return unserialize(base64_decode($this->request->{$method.'Var'}('opauth'))); |
||
389 | } |
||
390 | |||
391 | public function Link($action = null) { |
||
392 | return Controller::join_links( |
||
393 | self::config()->opauth_path, |
||
394 | $action |
||
395 | ); |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * 'path' param for use in Opauth's config |
||
400 | * MUST have trailling slash for Opauth needs |
||
401 | * @return string |
||
402 | */ |
||
403 | public static function get_path() { |
||
404 | return Controller::join_links( |
||
405 | self::config()->opauth_path, |
||
406 | 'strategy/' |
||
407 | ); |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * 'callback_url' param for use in Opauth's config |
||
412 | * MUST have trailling slash for Opauth needs |
||
413 | * @return string |
||
414 | */ |
||
415 | public static function get_callback_path() { |
||
416 | return Controller::join_links( |
||
417 | self::config()->opauth_path, |
||
418 | 'finished/' |
||
419 | ); |
||
420 | } |
||
421 | |||
422 | ////**** Template variables ****//// |
||
423 | function Title() { |
||
0 ignored issues
–
show
|
|||
424 | if($this->action == 'profilecompletion') { |
||
425 | return _t('OpauthController.PROFILECOMPLETIONTITLE', 'Complete your profile'); |
||
426 | } |
||
427 | return _t('OpauthController.TITLE', 'Social Login'); |
||
428 | } |
||
429 | |||
430 | public function Form() { |
||
431 | return $this->RegisterForm(); |
||
432 | } |
||
433 | ////**** END Template variables ****//// |
||
434 | |||
435 | } |
||
436 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.