This repository has been archived by the owner on May 29, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
<?php | ||
namespace FOC\Authenticate\Auth; | ||
|
||
use Cake\Auth\BaseAuthenticate; | ||
use Cake\Controller\ComponentRegistry; | ||
use Cake\Network\Request; | ||
use Cake\Network\Response; | ||
use Cake\ORM\TableRegistry; | ||
use Cake\Utility\Security; | ||
use \JWT; | ||
|
||
/** | ||
* An authentication adapter for authenticating using JSON Web Tokens. | ||
* | ||
* {{{ | ||
* $this->Auth->config('authenticate', [ | ||
* 'FOC/Authenticate.Jwt' => [ | ||
* 'parameter' => '_token', | ||
* 'userModel' => 'Users', | ||
* 'scope' => ['User.active' => 1] | ||
* 'fields' => [ | ||
* 'id' => 'id', | ||
* ], | ||
* ] | ||
* ]); | ||
* }}} | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* | ||
* @see http://jwt.io | ||
* @see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token | ||
*/ | ||
class JwtAuthenticate extends BaseAuthenticate { | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* Settings for this object. | ||
* | ||
* - `parameter` - The url parameter name of the token. Defaults to `_token`. | ||
* First $_SERVER['HTTP_BEARER'] is checked for token value. If empty this | ||
* query string paramater is checked. | ||
* - `userModel` - The model name of the User, defaults to `Users`. | ||
* - `fields` - Has key `id` whose value contains primary key field name. | ||
* Defaults to ['id' => 'id']. | ||
* - `scope` - Additional conditions to use when looking up and authenticating | ||
* users, i.e. `['Users.is_active' => 1].` | ||
* - `contain` - Extra models to contain. | ||
* | ||
* @param \Cake\Controller\ComponentRegistry $registry The Component registry | ||
* used on this request. | ||
* @param array $config Array of config to use. | ||
*/ | ||
public function __construct(ComponentRegistry $registry, $config) { | ||
$this->config([ | ||
'parameter' => '_token', | ||
'fields' => ['id' => 'id'], | ||
]); | ||
|
||
parent::__construct($registry, $config); | ||
} | ||
|
||
/** | ||
* Unused since this is a stateless authentication provider. | ||
* | ||
* @param Request $request The request object. | ||
* @param Response $response response object. | ||
* @return boolean Always false. | ||
*/ | ||
public function authenticate(Request $request, Response $response) { | ||
return false; | ||
} | ||
|
||
/** | ||
* Get token information from the request. | ||
* | ||
* @param \Cake\Network\Request $request Request object. | ||
* @return bool|array Either false or an array of user information | ||
*/ | ||
public function getUser(Request $request) { | ||
$token = $request->env('HTTP_BEARER'); | ||
if ($token) { | ||
return $this->_findUser($token); | ||
} | ||
|
||
if (!empty($this->_config['parameter']) && | ||
$token = $request->query($this->_config['parameter']) | ||
) { | ||
return $this->_findUser($token); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Find a user record. | ||
* | ||
* @param string $token The token identifier. | ||
* @param string $password Unused password. | ||
* @return bool|array Either false on failure, or an array of user data. | ||
*/ | ||
protected function _findUser($token, $password = null) { | ||
$token = JWT::decode($token, Security::salt()); | ||
|
||
// Token has full user record. | ||
if (isset($token->record)) { | ||
// Trick to convert object of stdClass to array. Typecasting to | ||
// array doesn't convert property values which are themselves objects. | ||
return json_decode(json_encode($token->record), true); | ||
} | ||
|
||
$userModel = $this->_config['userModel']; | ||
list($plugin, $model) = pluginSplit($userModel); | ||
$fields = $this->_config['fields']; | ||
|
||
$conditions = [$model . '.' . $fields['id'] => $token->id]; | ||
if (!empty($this->_config['scope'])) { | ||
$conditions = array_merge($conditions, $this->_config['scope']); | ||
} | ||
$table = TableRegistry::get($userModel)->find('all'); | ||
if ($this->_config['contain']) { | ||
$table = $table->contain($contain); | ||
} | ||
|
||
$result = $table | ||
->where($conditions) | ||
->hydrate(false) | ||
->first(); | ||
|
||
if (empty($result)) { | ||
return false; | ||
} | ||
|
||
unset($result[$fields['password']]); | ||
return $result; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
<?php | ||
namespace FOC\Authenticate\Auth\Test\TestCase\Auth; | ||
|
||
use Cake\Controller\Component\AuthComponent; | ||
use Cake\Controller\Controller; | ||
use Cake\I18n\Time; | ||
use Cake\Network\Request; | ||
use Cake\Network\Response; | ||
use Cake\ORM\TableRegistry; | ||
use Cake\TestSuite\TestCase; | ||
use Cake\Utility\Security; | ||
use FOC\Authenticate\Auth\JwtAuthenticate; | ||
use \JWT; | ||
|
||
/** | ||
* Test case for JwtAuthentication | ||
*/ | ||
class JwtAuthenticateTest extends TestCase { | ||
|
||
public $fixtures = ['plugin.FOC\Authenticate.multi_user']; | ||
|
||
/** | ||
* setup | ||
* | ||
* @return void | ||
*/ | ||
public function setUp() { | ||
parent::setUp(); | ||
|
||
$this->Registry = $this->getMock('Cake\Controller\ComponentRegistry'); | ||
$this->auth = new JwtAuthenticate($this->Registry, [ | ||
'userModel' => 'MultiUsers' | ||
]); | ||
|
||
$this->token = JWT::encode(['id' => 1], Security::salt()); | ||
|
||
$this->response = $this->getMock('Cake\Network\Response'); | ||
} | ||
|
||
/** | ||
* test authenticate token as query parameter | ||
* | ||
* @return void | ||
*/ | ||
public function testAuthenticateTokenParameter() { | ||
$request = new Request('posts/index'); | ||
|
||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertFalse($result); | ||
|
||
$expected = array( | ||
'id' => 1, | ||
'user_name' => 'mariano', | ||
'email' => 'mariano@example.com', | ||
'token' => '12345', | ||
'created' => new Time('2007-03-17 01:16:23'), | ||
'updated' => new Time('2007-03-17 01:18:31') | ||
); | ||
$request = new Request('posts/index?_token=' . $this->token); | ||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertEquals($expected, $result); | ||
|
||
$this->auth->config('parameter', 'tokenname'); | ||
$request = new Request('posts/index?tokenname=' . $this->token); | ||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertEquals($expected, $result); | ||
} | ||
|
||
/** | ||
* test authenticate token as request header | ||
* | ||
* @return void | ||
*/ | ||
public function testAuthenticateTokenHeader() { | ||
$request = new Request('posts/index'); | ||
|
||
$expected = array( | ||
'id' => 1, | ||
'user_name' => 'mariano', | ||
'email' => 'mariano@example.com', | ||
'token' => '12345', | ||
'created' => new Time('2007-03-17 01:16:23'), | ||
'updated' => new Time('2007-03-17 01:18:31') | ||
); | ||
$request->env('HTTP_BEARER', $this->token); | ||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertEquals($expected, $result); | ||
|
||
$this->setExpectedException('UnexpectedValueException'); | ||
$request->env('HTTP_BEARER', '66666'); | ||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertFalse($result); | ||
} | ||
|
||
/** | ||
* test authenticate token with user record | ||
* | ||
* @return void | ||
*/ | ||
public function testAuthenticateWithUserRecord() { | ||
$request = new Request('posts/index'); | ||
|
||
$expected = [ | ||
'id' => 99, | ||
'username' => 'ADmad', | ||
'group' => ['name' => 'admin'] | ||
]; | ||
$request->env('HTTP_BEARER', JWT::encode(['record' => $expected], Security::salt())); | ||
$result = $this->auth->getUser($request, $this->response); | ||
$this->assertEquals($expected, $result); | ||
} | ||
|
||
} |