Skip to content
This repository was archived by the owner on Nov 9, 2018. It is now read-only.

Encrypt secrets in database #33

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Config/Migration/1376749679_enlarge_secrets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

class EnlargeSecrets extends CakeMigration {

/**
* Migration description
*
* @var string
* @access public
*/
public $description = 'Enlarge client_secret field to support encryption';

/**
* Actions to be performed
*
* @var array $migration
* @access public
*/
public $migration = array(
'up' => 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;
}

}
80 changes: 80 additions & 0 deletions Config/Migration/1376879144_clients_add_name_date_fields.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

class ClientsAddNameDateFields extends CakeMigration {

/**
* Migration description
*
* @var string
* @access public
*/
public $description = '';

/**
* Actions to be performed
*
* @var array $migration
* @access public
*/
public $migration = array(
'up' => 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;
}

}
130 changes: 130 additions & 0 deletions Console/Command/ClientsShell.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

App::uses('OAuthUtility', 'OAuth.Lib');
App::uses('Shell', 'Console');
App::uses('Validation', 'Utility');

/**
* Client utility shell class
*/
class ClientsShell extends Shell {

/**
* Models used by this shell
*/
public $uses = array(
'OAuth.Client',
);

/**
* Configure option parser
*/
public function getOptionParser() {
return parent::getOptionParser()
->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('<error>Unable to add client record</error>');
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();
}

}
13 changes: 8 additions & 5 deletions Controller/Component/OAuthComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
41 changes: 41 additions & 0 deletions Lib/OAuthUtility.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

App::uses('Security', 'Utility');

class OAuthUtility {

/**
* Convenience method to encrypt strings using Security::rijndael()
*
* @param string $text Text to encrypt
* @param string $key Encryption key. When null, `Security.salt` will be used
* @return string Encrypted string
*/
public static function encrypt($text, $key = null) {
$key = $key === null ? Configure::read('Security.salt') : $key;
return base64_encode(Security::rijndael($text, $key, 'encrypt'));
}

/**
* Convenience method to decrypt strings using Security::rijndael()
*
* @param string $text Text to decrypt
* @param string $key Decryption key. When null, `Security.salt` will be used
* @return string Decrypted string
*/
public static function decrypt($text, $key = null) {
$key = $key === null ? Configure::read('Security.salt') : $key;
return Security::rijndael(base64_decode($text), $key, 'decrypt');
}

/**
* Convenience method to Security::hash()
*
* @param string $text String to secure
* @return string Hashed string
*/
public static function hash($text) {
return Security::hash($text, null, true);
}

}
6 changes: 3 additions & 3 deletions Model/Behavior/HashedFieldBehavior.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

App::uses('ModelBehavior', 'Model');
App::uses('Security', 'Utility');
App::uses('OAuthUtility', 'OAuth.Lib');

/**
* Enable automatic field hashing when in Model::save() Model::find()
Expand Down Expand Up @@ -31,7 +31,7 @@ public function beforeSave(Model $Model) {
foreach ((array) $setting['fields'] as $field) {
if (array_key_exists($field, $Model->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;
Expand All @@ -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;
Expand Down
Loading