diff --git a/Config/Migration/1376749679_enlarge_secrets.php b/Config/Migration/1376749679_enlarge_secrets.php new file mode 100644 index 0000000..95b8ac2 --- /dev/null +++ b/Config/Migration/1376749679_enlarge_secrets.php @@ -0,0 +1,59 @@ + array( + 'alter_field' => array( + 'clients' => array( + 'client_secret' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 132, 'collate' => 'utf8_general_ci', 'charset' => 'utf8', 'after' => 'client_id'), + ), + ), + ), + + 'down' => array( + 'alter_field' => array( + 'clients' => array( + 'client_secret' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_general_ci', 'charset' => 'utf8', 'after' => 'client_id'), + ), + ), + ), + ); + +/** + * Before migration callback + * + * @param string $direction, up or down direction of migration process + * @return boolean Should process continue + * @access public + */ + public function before($direction) { + return true; + } + +/** + * After migration callback + * + * @param string $direction, up or down direction of migration process + * @return boolean Should process continue + * @access public + */ + public function after($direction) { + return true; + } + +} diff --git a/Config/Migration/1376879144_clients_add_name_date_fields.php b/Config/Migration/1376879144_clients_add_name_date_fields.php new file mode 100644 index 0000000..ed12008 --- /dev/null +++ b/Config/Migration/1376879144_clients_add_name_date_fields.php @@ -0,0 +1,80 @@ + array( + 'create_field' => array( + 'clients' => array( + 'name' => array( + 'type' => 'string', + 'null' => false, + 'default' => null, + 'length' => 256, + 'collate' => + 'utf8_general_ci', + 'charset' => 'utf8', + 'after' => 'client_id', + ), + 'created' => array( + 'type' => 'datetime', + 'after' => 'user_id', + 'null' => true, + ), + 'modified' => array( + 'type' => 'datetime', + 'after' => 'created', + 'null' => true, + ), + ), + ), + ), + + 'down' => array( + 'drop_field' => array( + 'clients' => array( + 'name', + 'created', + 'modified', + ), + ), + ), + ); + +/** + * Before migration callback + * + * @param string $direction, up or down direction of migration process + * @return boolean Should process continue + * @access public + */ + public function before($direction) { + return true; + } + +/** + * After migration callback + * + * @param string $direction, up or down direction of migration process + * @return boolean Should process continue + * @access public + */ + public function after($direction) { + return true; + } + +} diff --git a/Console/Command/ClientsShell.php b/Console/Command/ClientsShell.php new file mode 100644 index 0000000..09a4918 --- /dev/null +++ b/Console/Command/ClientsShell.php @@ -0,0 +1,130 @@ +description('Client Utility') + ->addSubCommand('list', array( + 'help' => 'List existing client records', + 'parser' => array( + 'options' => array( + 'secret' => array( + 'help' => 'Display secrets', + 'short' => 's', + 'boolean' => true, + ), + ), + ), + )) + ->addSubCommand('add', array( + 'help' => 'Add a new client', + 'parser' => array( + 'options' => array( + 'name' => array( + 'required' => true, + 'help' => 'Client Name', + 'short' => 'n', + ), + 'redirect_uri' => array( + 'required' => true, + 'help' => 'Redirect URI', + 'short' => 'u', + ), + ), + ), + )); + } + +/** + * Shell entry point + */ + public function main() { + $method = null; + if (isset($this->args[0])) { + $method = $this->args[0]; + } + + switch ($method) { + case 'list': + $this->_clients(); + break; + + default: + $this->_displayHelp(); + break; + } + } + +/** + * List all client records + */ + protected function _clients() { + $clients = $this->Client->find('all', array( + 'recursive' => -1, + )); + $this->out(""); + foreach ($clients as $data) { + $client = $data['Client']; + $this->out(sprintf('%-15s: %s', 'Client Id', $client['client_id'])); + $this->out(sprintf('%-15s: %s', 'Client Name', $client['name'])); + if ($this->params['secret']) { + $secret = OAuthUtility::decrypt($client['client_secret']); + $this->out(sprintf('%-15s: %s', 'Client Secret', $secret)); + } + $this->out(sprintf('%-15s: %s', 'Redirect URI', $client['redirect_uri'])); + $this->out(""); + } + $this->out(sprintf('%d record(s) found', count($clients))); + } + +/** + * Add a new client record + */ + public function add() { + if (empty($this->params['name'])) { + return $this->error('Please provide `name`'); + } + if (empty($this->params['redirect_uri'])) { + return $this->error('Please provide `redirect_uri`'); + } + if (!Validation::url($this->params['redirect_uri'])) { + return $this->error('Please provide a valid `redirect_uri`'); + } + $client = $this->Client->create(array( + 'name' => $this->params['name'], + 'redirect_uri' => $this->params['redirect_uri'], + )); + $client = $this->Client->add($client); + if (!$client) { + $this->err('Unable to add client record'); + if (isset($this->Client->validationErrors)) { + $this->error('Validation error', print_r($this->Client->validationErrors, true)); + } + return; + } + $this->out("Client successfully added:\n"); + $this->out(sprintf("\tClient id: %s", $client['Client']['client_id'])); + $this->out(sprintf("\tClient name: %s", $client['Client']['name'])); + $this->out(sprintf("\tClient secret: %s", $this->Client->addClientSecret)); + $this->out(); + } + +} diff --git a/Controller/Component/OAuthComponent.php b/Controller/Component/OAuthComponent.php index df1fd6e..c15e472 100644 --- a/Controller/Component/OAuthComponent.php +++ b/Controller/Component/OAuthComponent.php @@ -326,6 +326,7 @@ public function user($field = null, $token = null) { * * @param string $password * @return string Hashed password + * @deprecated Will be removed in future version */ public static function hash($password) { return Security::hash($password, null, true); @@ -399,15 +400,17 @@ public function __call($name, $arguments) { */ public function checkClientCredentials($client_id, $client_secret = null) { $conditions = array('client_id' => $client_id); - if ($client_secret) { - $conditions['client_secret'] = $client_secret; - } $client = $this->Client->find('first', array( 'conditions' => $conditions, 'recursive' => -1 )); - if ($client) { - return $client['Client']; + if ($client && $client_secret) { + $decrypted = self::decrypt($client['Client']['client_secret']); + if ($decrypted == $client_secret) { + return $client['Client']; + } else { + return false; + } }; return false; } diff --git a/Lib/OAuthUtility.php b/Lib/OAuthUtility.php new file mode 100644 index 0000000..5b93a62 --- /dev/null +++ b/Lib/OAuthUtility.php @@ -0,0 +1,41 @@ +data[$Model->alias])) { $data = $Model->data[$Model->alias][$field]; - $Model->data[$Model->alias][$field] = Security::hash($data, null, true); + $Model->data[$Model->alias][$field] = OAuthUtility::hash($data); } } return true; @@ -52,7 +52,7 @@ public function beforeFind(Model $Model, $queryData) { } if (isset($queryField)) { $data = $conditions[$queryField]; - $conditions[$queryField] = Security::hash($data, null, true); + $conditions[$queryField] = OAuthUtility::hash($data); } } return $queryData; diff --git a/Model/Client.php b/Model/Client.php index 1331cbb..9a0e639 100644 --- a/Model/Client.php +++ b/Model/Client.php @@ -1,6 +1,7 @@ array( - 'fields' => array( - 'client_secret' - ), - ), - ); - /** * hasMany associations * @@ -161,7 +154,17 @@ public function newClientSecret() { while ($length--) { $str .= $chars[mt_rand(0, $count - 1)]; } - return OAuthComponent::hash($str); + return OAuthUtility::hash($str); + } + +/** + * Encrypt client secret + */ + public function beforeSave($options) { + if (isset($this->data[$this->alias]['client_secret'])) { + $this->data[$this->alias]['client_secret'] = OAuthUtility::encrypt($this->data[$this->alias]['client_secret']); + } + return true; } public function afterSave($created) { diff --git a/Model/OAuthAppModel.php b/Model/OAuthAppModel.php index 48201f4..a971dd8 100644 --- a/Model/OAuthAppModel.php +++ b/Model/OAuthAppModel.php @@ -1,5 +1,7 @@