OAuth provider support with functional demo/test.
Functional OAuth provider support including data storage of client/user tokens. Documentation for the Credential object. Gh-77 Gh-48 Gh-78
This commit is contained in:
parent
dbb27fdb13
commit
7674ba748d
11 changed files with 496 additions and 167 deletions
43
documentation/schemas/Credential.markdown
Normal file
43
documentation/schemas/Credential.markdown
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
Documentation
|
||||||
|
=======================
|
||||||
|
#### OpenPhoto, a photo service for the masses
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
### What's a Credential object for?
|
||||||
|
|
||||||
|
The Credential object stores permissioning OAuth tokens granted by the user to various applications.
|
||||||
|
|
||||||
|
Due to the distrubuted nature of the platform the model for applications and users is flattened and not relational. For every application there will be exactly 1 user.
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
### Schema for a User object
|
||||||
|
|
||||||
|
{
|
||||||
|
id: (string),
|
||||||
|
name: (string),
|
||||||
|
image: (string),
|
||||||
|
client_secret: (string),
|
||||||
|
user_token: (string),
|
||||||
|
user_secret: (string),
|
||||||
|
permissions: (set),
|
||||||
|
verifier: (string),
|
||||||
|
type: (enum),
|
||||||
|
status: (bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
### Schema description
|
||||||
|
|
||||||
|
* id, A (quasi-)public token which identifies the application
|
||||||
|
* name, A human readable name describing the client
|
||||||
|
* image, The base64 encoded version of a 100x100 pixel image
|
||||||
|
* client_secret, A shared secret used to verify requests originated from the application
|
||||||
|
* user_token, A (quasi-)public token which idenfies the user
|
||||||
|
* user_secret, A shared secret to verify that the request originated from the application for the user
|
||||||
|
* permissions, A set of permissions the credential has (create, read, write, delete)
|
||||||
|
* verifier, A verification string to ensure that the `oauth_callback` parameter wasn't spoofed
|
||||||
|
* type, An enumerated field specifying the type of token (unauthorized_request, request, access)
|
||||||
|
* status, Numeric representation of the status (0=deleted, 1=active)
|
13
src/html/assets/themes/default/templates/oauthApprove.php
Normal file
13
src/html/assets/themes/default/templates/oauthApprove.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<h1>Would you like ot grant <?php echo $consumer['name']; ?> access to your account?</h1>
|
||||||
|
<p>
|
||||||
|
<h2>You are providing the following permissions</h2>
|
||||||
|
<ul>
|
||||||
|
<?php foreach($consumer['permissions'] as $permission) { ?>
|
||||||
|
<li><?php echo $permission; ?></li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="client_key" value="<?php Utility::safe($consumer['id']); ?>">
|
||||||
|
<button type="submit">Approve</button>
|
||||||
|
</form>
|
15
src/html/assets/themes/default/templates/oauthCreate.php
Normal file
15
src/html/assets/themes/default/templates/oauthCreate.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<form method="post">
|
||||||
|
<ul>
|
||||||
|
<li>Name this app: <input type="text" name="name"></li>
|
||||||
|
<li>Permissions:
|
||||||
|
<ul>
|
||||||
|
<li><input type="checkbox" name="permissions" value="read" checked="true"> Read</li>
|
||||||
|
<li><input type="checkbox" name="permissions" value="create"> Create</li>
|
||||||
|
<li><input type="checkbox" name="permissions" value="update"> Update</li>
|
||||||
|
<li><input type="checkbox" name="permissions" value="delete"> Delete</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<input type="hidden" name="oauth_callback" value="<?php Utility::safe($callback); ?>">
|
||||||
|
<button type="submit">Create</button>
|
||||||
|
</form>
|
||||||
|
|
|
@ -12,6 +12,7 @@ interface DatabaseInterface
|
||||||
public function deletePhoto($id);
|
public function deletePhoto($id);
|
||||||
public function deleteAction($id);
|
public function deleteAction($id);
|
||||||
// get methods read
|
// get methods read
|
||||||
|
public function getCredential($id);
|
||||||
public function getPhotoNextPrevious($id);
|
public function getPhotoNextPrevious($id);
|
||||||
public function getPhoto($id);
|
public function getPhoto($id);
|
||||||
public function getPhotoWithActions($id);
|
public function getPhotoWithActions($id);
|
||||||
|
@ -20,6 +21,7 @@ interface DatabaseInterface
|
||||||
public function getTag($tag);
|
public function getTag($tag);
|
||||||
public function getTags($filter = array());
|
public function getTags($filter = array());
|
||||||
// post methods update
|
// post methods update
|
||||||
|
public function postCredential($id, $params);
|
||||||
public function postPhoto($id, $params);
|
public function postPhoto($id, $params);
|
||||||
public function postUser($id, $params);
|
public function postUser($id, $params);
|
||||||
public function postTag($id, $params);
|
public function postTag($id, $params);
|
||||||
|
@ -27,6 +29,7 @@ interface DatabaseInterface
|
||||||
public function postTagsCounter($params);
|
public function postTagsCounter($params);
|
||||||
// put methods create but do not update
|
// put methods create but do not update
|
||||||
public function putAction($id, $params);
|
public function putAction($id, $params);
|
||||||
|
public function putCredential($id, $params);
|
||||||
public function putPhoto($id, $params);
|
public function putPhoto($id, $params);
|
||||||
public function putUser($id, $params);
|
public function putUser($id, $params);
|
||||||
public function putTag($id, $params);
|
public function putTag($id, $params);
|
||||||
|
|
|
@ -49,6 +49,32 @@ class DatabaseMySql implements DatabaseInterface
|
||||||
return ($res == 1);
|
return ($res == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a credential with $id
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to get
|
||||||
|
* @return mixed Array on success, FALSE on failure
|
||||||
|
*/
|
||||||
|
public function getCredential($id)
|
||||||
|
{
|
||||||
|
// TODO: fill this in Gh-78
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a photo specified by $id
|
||||||
|
*
|
||||||
|
* @param string $id ID of the photo to retrieve
|
||||||
|
* @return mixed Array on success, FALSE on failure
|
||||||
|
*/
|
||||||
|
public function getPhoto($id)
|
||||||
|
{
|
||||||
|
$photo = getDatabase()->one("SELECT * FROM photo WHERE id=:id", array(':id' => $id));
|
||||||
|
if(empty($photo))
|
||||||
|
return false;
|
||||||
|
return self::normalizePhoto($photo);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the next and previous photo surrounding photo with $id
|
* Retrieve the next and previous photo surrounding photo with $id
|
||||||
*
|
*
|
||||||
|
@ -73,20 +99,6 @@ class DatabaseMySql implements DatabaseInterface
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a photo specified by $id
|
|
||||||
*
|
|
||||||
* @param string $id ID of the photo to retrieve
|
|
||||||
* @return mixed Array on success, FALSE on failure
|
|
||||||
*/
|
|
||||||
public function getPhoto($id)
|
|
||||||
{
|
|
||||||
$photo = getDatabase()->one("SELECT * FROM photo WHERE id=:id", array(':id' => $id));
|
|
||||||
if(empty($photo))
|
|
||||||
return false;
|
|
||||||
return self::normalizePhoto($photo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a photo from the database and include the actions on the photo.
|
* Retrieve a photo from the database and include the actions on the photo.
|
||||||
* Actions are stored in a separate domain so the calls need to be made in parallel
|
* Actions are stored in a separate domain so the calls need to be made in parallel
|
||||||
|
@ -221,6 +233,20 @@ class DatabaseMySql implements DatabaseInterface
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the information for an existing credential.
|
||||||
|
* This method overwrites existing values present in $params.
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to update.
|
||||||
|
* @param array $params Attributes to update.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function postCredential($id, $params)
|
||||||
|
{
|
||||||
|
// TODO: fill this in Gh-78
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the information for an existing photo.
|
* Update the information for an existing photo.
|
||||||
* This method overwrites existing values present in $params.
|
* This method overwrites existing values present in $params.
|
||||||
|
@ -362,6 +388,20 @@ class DatabaseMySql implements DatabaseInterface
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new credential to the database
|
||||||
|
* This method does not overwrite existing values present in $params - hence "new credential".
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to update which is always 1.
|
||||||
|
* @param array $params Attributes to update.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function putCredential($id, $params)
|
||||||
|
{
|
||||||
|
// TODO: fill this in Gh-78
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new photo to the database
|
* Add a new photo to the database
|
||||||
* This method does not overwrite existing values present in $params - hence "new photo".
|
* This method does not overwrite existing values present in $params - hence "new photo".
|
||||||
|
|
|
@ -11,7 +11,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
* Member variables holding the names to the SimpleDb domains needed and the database object itself.
|
* Member variables holding the names to the SimpleDb domains needed and the database object itself.
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
private $db, $domainAction, $domainPhoto, $domainTag, $domainUser;
|
private $db, $domainAction, $domainCredential, $domainPhoto, $domainTag, $domainUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -23,6 +23,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
$this->db = new AmazonSDB(getConfig()->get('credentials')->awsKey, getConfig()->get('credentials')->awsSecret);
|
$this->db = new AmazonSDB(getConfig()->get('credentials')->awsKey, getConfig()->get('credentials')->awsSecret);
|
||||||
$this->domainPhoto = getConfig()->get('aws')->simpleDbDomain;
|
$this->domainPhoto = getConfig()->get('aws')->simpleDbDomain;
|
||||||
$this->domainAction = getConfig()->get('aws')->simpleDbDomain.'Action';
|
$this->domainAction = getConfig()->get('aws')->simpleDbDomain.'Action';
|
||||||
|
$this->domainCredential = getConfig()->get('aws')->simpleDbDomain.'Credential';
|
||||||
$this->domainUser = getConfig()->get('aws')->simpleDbDomain.'User';
|
$this->domainUser = getConfig()->get('aws')->simpleDbDomain.'User';
|
||||||
$this->domainTag = getConfig()->get('aws')->simpleDbDomain.'Tag';
|
$this->domainTag = getConfig()->get('aws')->simpleDbDomain.'Tag';
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,36 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
return $res->isOK();
|
return $res->isOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a credential with $id
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to get
|
||||||
|
* @return mixed Array on success, FALSE on failure
|
||||||
|
*/
|
||||||
|
public function getCredential($id)
|
||||||
|
{
|
||||||
|
$res = $this->db->select("SELECT * FROM `{$this->domainCredential}` WHERE itemName()='{$id}' AND status='1'", array('ConsistentRead' => 'true'));
|
||||||
|
if(isset($res->body->SelectResult->Item))
|
||||||
|
return self::normalizeCredential($res->body->SelectResult->Item);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a photo specified by $id
|
||||||
|
*
|
||||||
|
* @param string $id ID of the photo to retrieve
|
||||||
|
* @return mixed Array on success, FALSE on failure
|
||||||
|
*/
|
||||||
|
public function getPhoto($id)
|
||||||
|
{
|
||||||
|
$res = $this->db->select("SELECT * FROM `{$this->domainPhoto}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true'));
|
||||||
|
if(isset($res->body->SelectResult->Item))
|
||||||
|
return self::normalizePhoto($res->body->SelectResult->Item);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the next and previous photo surrounding photo with $id
|
* Retrieve the next and previous photo surrounding photo with $id
|
||||||
*
|
*
|
||||||
|
@ -88,22 +119,6 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a photo specified by $id
|
|
||||||
*
|
|
||||||
* @param string $id ID of the photo to retrieve
|
|
||||||
* @return mixed Array on success, FALSE on failure
|
|
||||||
*/
|
|
||||||
public function getPhoto($id)
|
|
||||||
{
|
|
||||||
$res = $this->db->select("SELECT * FROM `{$this->domainPhoto}` WHERE itemName()='{$id}'", array('ConsistentRead' => 'true'));
|
|
||||||
if(isset($res->body->SelectResult->Item))
|
|
||||||
return self::normalizePhoto($res->body->SelectResult->Item);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a photo from the database and include the actions on the photo.
|
* Retrieve a photo from the database and include the actions on the photo.
|
||||||
* Actions are stored in a separate domain so the calls need to be made in parallel
|
* Actions are stored in a separate domain so the calls need to be made in parallel
|
||||||
|
@ -269,6 +284,20 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the information for an existing credential.
|
||||||
|
* This method overwrites existing values present in $params.
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to update.
|
||||||
|
* @param array $params Attributes to update.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function postCredential($id, $params)
|
||||||
|
{
|
||||||
|
$res = $this->db->put_attributes($this->domainCredential, $id, $params, true);
|
||||||
|
return $res->isOK();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the information for an existing photo.
|
* Update the information for an existing photo.
|
||||||
* This method overwrites existing values present in $params.
|
* This method overwrites existing values present in $params.
|
||||||
|
@ -401,6 +430,20 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
return $res->isOK();
|
return $res->isOK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new credential to the database
|
||||||
|
* This method does not overwrite existing values present in $params - hence "new credential".
|
||||||
|
*
|
||||||
|
* @param string $id ID of the credential to update which is always 1.
|
||||||
|
* @param array $params Attributes to update.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function putCredential($id, $params)
|
||||||
|
{
|
||||||
|
$res = $this->db->put_attributes($this->domainCredential, $id, $params);
|
||||||
|
return $res->isOK();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new photo to the database
|
* Add a new photo to the database
|
||||||
* This method does not overwrite existing values present in $params - hence "new photo".
|
* This method does not overwrite existing values present in $params - hence "new photo".
|
||||||
|
@ -461,6 +504,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
|
|
||||||
$queue = new CFBatchRequest();
|
$queue = new CFBatchRequest();
|
||||||
$this->db->batch($queue)->create_domain($this->domainAction);
|
$this->db->batch($queue)->create_domain($this->domainAction);
|
||||||
|
$this->db->batch($queue)->create_domain($this->domainCredential);
|
||||||
$this->db->batch($queue)->create_domain($this->domainPhoto);
|
$this->db->batch($queue)->create_domain($this->domainPhoto);
|
||||||
$this->db->batch($queue)->create_domain($this->domainTag);
|
$this->db->batch($queue)->create_domain($this->domainTag);
|
||||||
$this->db->batch($queue)->create_domain($this->domainUser);
|
$this->db->batch($queue)->create_domain($this->domainUser);
|
||||||
|
@ -504,6 +548,28 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
return $action;
|
return $action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes data from simpleDb into schema definition
|
||||||
|
*
|
||||||
|
* @param SimpleXMLObject $raw An action from SimpleDb in SimpleXML.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function normalizeCredential($raw)
|
||||||
|
{
|
||||||
|
$credential = array();
|
||||||
|
$credential['id'] = strval($raw->Name);
|
||||||
|
foreach($raw->Attribute as $item)
|
||||||
|
{
|
||||||
|
$name = (string)$item->Name;
|
||||||
|
$value = (string)$item->Value;
|
||||||
|
if($name == 'permissions')
|
||||||
|
$credential[$name] = (array)explode(',', $value);
|
||||||
|
else
|
||||||
|
$credential[$name] = $value;
|
||||||
|
}
|
||||||
|
return $credential;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes data from simpleDb into schema definition
|
* Normalizes data from simpleDb into schema definition
|
||||||
*
|
*
|
||||||
|
@ -566,6 +632,7 @@ class DatabaseSimpleDb implements DatabaseInterface
|
||||||
private function normalizeUser($raw)
|
private function normalizeUser($raw)
|
||||||
{
|
{
|
||||||
$user = array();
|
$user = array();
|
||||||
|
$user['id'] = strval($raw->Name);
|
||||||
foreach($raw->Attribute as $item)
|
foreach($raw->Attribute as $item)
|
||||||
{
|
{
|
||||||
$name = (string)$item->Name;
|
$name = (string)$item->Name;
|
||||||
|
|
|
@ -3,7 +3,9 @@ class OAuthController extends BaseController
|
||||||
{
|
{
|
||||||
public static function authorize()
|
public static function authorize()
|
||||||
{
|
{
|
||||||
$callback = $verifier = null;
|
// TODO require login
|
||||||
|
// TODO require SSL
|
||||||
|
$callback = null;
|
||||||
$separator = '?';
|
$separator = '?';
|
||||||
|
|
||||||
if(isset($_GET['oauth_callback']))
|
if(isset($_GET['oauth_callback']))
|
||||||
|
@ -13,55 +15,158 @@ class OAuthController extends BaseController
|
||||||
$separator = '&';
|
$separator = '&';
|
||||||
}
|
}
|
||||||
|
|
||||||
$callback .= "{$separator}oauth_token=token&oauth_verifier=verifier";
|
// if an oauth_token is passed then display the approval screen else ask to create a credential
|
||||||
echo sprintf('<a href="%s">Click here to allow and continue</a>', $callback);
|
if(isset($_GET['oauth_token']))
|
||||||
|
{
|
||||||
|
$consumer = getCredential()->getConsumer($_GET['oauth_token']);
|
||||||
|
if(!$consumer)
|
||||||
|
{
|
||||||
|
// TODO templatize this
|
||||||
|
echo sprintf('Could not find consumer for token %s', $_GET['oauth_token']);
|
||||||
|
}
|
||||||
|
else if($consumer['type'] != Credential::typeUnauthorizedRequest)
|
||||||
|
{
|
||||||
|
// TODO templatize this
|
||||||
|
echo sprintf('This token has been approved or is invalid %s', $_GET['oauth_token']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$body = getTheme()->get('oauthApprove.php', array('consumer' => $consumer));
|
||||||
|
getTheme()->display('template.php', array('body' => $body, 'page' => 'oauth-approve'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$body = getTheme()->get('oauthCreate.php', array('callback' => $callback));
|
||||||
|
getTheme()->display('template.php', array('body' => $body, 'page' => 'oauth-create'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function authorizePost()
|
||||||
|
{
|
||||||
|
// TODO require login
|
||||||
|
// TODO require SSL
|
||||||
|
if(isset($_GET['oauth_token']) && !empty($_GET['oauth_token']))
|
||||||
|
{
|
||||||
|
$token = $_GET['oauth_token'];
|
||||||
|
// if an oauth_token exists then the user wants to approve it
|
||||||
|
// change the status from unauthorized_request to request
|
||||||
|
$token = $_GET['oauth_token'];
|
||||||
|
$res = getDb()->postCredential($token, array('type' => Credential::typeRequest));
|
||||||
|
if(!$res)
|
||||||
|
{
|
||||||
|
// TODO templatize this
|
||||||
|
echo sprintf('Could not convert this unauthorized request token to a request token %s', $_GET['oauth_token']);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$consumer = getDb()->getCredential($token);
|
||||||
|
$callback = null;
|
||||||
|
$separator = '?';
|
||||||
|
|
||||||
|
if(isset($_GET['oauth_callback']))
|
||||||
|
{
|
||||||
|
$callback = $_GET['oauth_callback'];
|
||||||
|
if(stripos($callback, '?') !== false)
|
||||||
|
$separator = '&';
|
||||||
|
}
|
||||||
|
$callback .= "{$separator}&oauth_token={$token}&oauth_verifier={$consumer['verifier']}";
|
||||||
|
// TODO require SSL unless omited in the config
|
||||||
|
getRoute()->redirect($callback, null, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no oauth token so this call is to create a credential
|
||||||
|
$clientToken = getCredential()->add($_POST['name'], (array)explode(',', $_POST['permissions']));
|
||||||
|
if(!$clientToken)
|
||||||
|
getLogger()->warn(sprintf('Could not add credential for: %s', json_encode($_POST)));
|
||||||
|
|
||||||
|
$callback = urlencode($_POST['oauth_callback']);
|
||||||
|
getRoute()->redirect("/v1/oauth/authorize?oauth_token={$clientToken}&oauth_callback={$callback}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function flow()
|
public static function flow()
|
||||||
{
|
{
|
||||||
if(!isset($_GET['oauth_token']))
|
if(isset($_GET['oauth_token']))
|
||||||
{
|
|
||||||
$ch = curl_init('http://opme/v1/oauth/token/request');
|
|
||||||
curl_setopt($ch, CURLOPT_POST, 1);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array());
|
|
||||||
$tok = curl_exec($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
parse_str($tok);
|
|
||||||
$callback = sprintf('http://%s/v1/oauth/flow', $_SERVER['HTTP_HOST']);
|
|
||||||
echo sprintf('<a href="http://opme/v1/oauth/authorize?oauth_token=%s&oauth_callback=%s">Get request token</a>', $oauth_token, urlencode($callback));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
$token = $_GET['oauth_token'];
|
||||||
|
$verifier = $_GET['oauth_verifier'];
|
||||||
$ch = curl_init('http://opme/v1/oauth/token/access');
|
$ch = curl_init('http://opme/v1/oauth/token/access');
|
||||||
curl_setopt($ch, CURLOPT_POST, 1);
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, array('oauth_token' => $_GET['oauth_token']));
|
curl_setopt($ch, CURLOPT_POSTFIELDS, array('oauth_token' => $token, 'oauth_verifier' => $verifier));
|
||||||
$tok = curl_exec($ch);
|
$tok = curl_exec($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
parse_str($tok);
|
parse_str($tok);
|
||||||
echo sprintf('You exchanged a request token for an access token which is (%s, %s)', $oauth_token, $oauth_token_secret);
|
setcookie('oauth', $tok);
|
||||||
|
echo sprintf('You exchanged a request token for an access token<br><a href="?reloaded=1">Reload to make an OAuth request</a>', $oauth_token, $oauth_token_secret);
|
||||||
|
}
|
||||||
|
else if(!isset($_GET['reloaded']))
|
||||||
|
{
|
||||||
|
$callback = sprintf('http://%s/v1/oauth/flow', $_SERVER['HTTP_HOST']);
|
||||||
|
echo sprintf('<a href="http://opme/v1/oauth/authorize?oauth_callback=%s">Create a new client id</a>', urlencode($callback));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
parse_str($_COOKIE['oauth']);
|
||||||
|
$consumer = getDb()->getCredential($oauth_token);
|
||||||
|
$oauth = new OAuth($oauth_token,$oauth_token_secret,OAUTH_SIG_METHOD_HMACSHA1,OAUTH_AUTH_TYPE_AUTHORIZATION);
|
||||||
|
$oauth->setToken($user_token,$user_secret);
|
||||||
|
$oauth->fetch(sprintf('http://%s/v1/oauth/test?oauth_consumer_key=%s', $_SERVER['HTTP_HOST'], $oauth_token));
|
||||||
|
$response_info = $oauth->getLastResponseInfo();
|
||||||
|
header("Content-Type: {$response_info["content_type"]}");
|
||||||
|
echo $oauth->getLastResponse();
|
||||||
|
} catch(OAuthException $E) {
|
||||||
|
echo "Exception caught!\n";
|
||||||
|
echo "Response: ". $E->lastResponse . "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function test()
|
public static function test()
|
||||||
{
|
{
|
||||||
$oauth = new Auth();
|
if(getCredential()->checkRequest())
|
||||||
$oauth->checkRequest();
|
{
|
||||||
|
echo "Good work! This request made a successful OAuth request.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "Boooo!!!! The OAuth request made FAILED :(.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tokenAccess()
|
public static function tokenAccess()
|
||||||
{
|
{
|
||||||
$oauth = new Auth('token','token');
|
// TODO require login
|
||||||
echo 'oauth_token=token&oauth_token_secret=token';
|
// TODO require SSL
|
||||||
|
// TODO check oauth_verifier
|
||||||
|
$token = $_POST['oauth_token'];
|
||||||
|
$verifier = $_POST['oauth_verifier'];
|
||||||
|
$consumer = getDb()->getCredential($token);
|
||||||
|
if(!$consumer || $consumer['verifier'] != $verifier)
|
||||||
|
{
|
||||||
|
echo 'oauth_error=could_not_authorize';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
getCredential()->addUserToken($consumer['id'], true);
|
||||||
|
$consumer = getDb()->getCredential($token);
|
||||||
|
echo "oauth_token={$consumer['id']}&oauth_token_secret={$consumer['client_secret']}&user_token={$consumer['user_token']}&user_secret={$consumer['user_secret']}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tokenRequest()
|
public static function tokenRequest()
|
||||||
{
|
{
|
||||||
$oauth = new Auth('token','token');
|
// TODO require login
|
||||||
|
// TODO require SSL
|
||||||
|
// Not yet implemented
|
||||||
$type = 'unauthorized';
|
$type = 'unauthorized';
|
||||||
if(isset($_GET['oauth_token']))
|
if(isset($_GET['oauth_token']))
|
||||||
|
{
|
||||||
$type = 'authorized';
|
$type = 'authorized';
|
||||||
|
}
|
||||||
echo "oauth_token=token&type={$type}";
|
echo "oauth_token=token&type={$type}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ require getConfig()->get('paths')->libraries . '/functions.php';
|
||||||
|
|
||||||
// models
|
// models
|
||||||
require getConfig()->get('paths')->models . '/Utility.php';
|
require getConfig()->get('paths')->models . '/Utility.php';
|
||||||
require getConfig()->get('paths')->models . '/Auth.php';
|
require getConfig()->get('paths')->models . '/Credential.php';
|
||||||
require getConfig()->get('paths')->models . '/Action.php';
|
require getConfig()->get('paths')->models . '/Action.php';
|
||||||
require getConfig()->get('paths')->models . '/Photo.php';
|
require getConfig()->get('paths')->models . '/Photo.php';
|
||||||
require getConfig()->get('paths')->models . '/Tag.php';
|
require getConfig()->get('paths')->models . '/Tag.php';
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
<?php
|
|
||||||
class Auth
|
|
||||||
{
|
|
||||||
private $provider;
|
|
||||||
|
|
||||||
public function checkRequest()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(!$this->initProvider())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$this->provider->consumerHandler(array($this,'getConsumer'));
|
|
||||||
$this->provider->timestampNonceHandler(array($this,'checkTimestampAndNonce'));
|
|
||||||
$this->provider->tokenHandler(array($this,'tokenHandler'));
|
|
||||||
$this->provider->setParam('__route__', null);
|
|
||||||
$this->provider->setRequestTokenPath('/v1/oauth/token/request'); // No token needed for this end point
|
|
||||||
$this->provider->checkOAuthRequest('http://opme/v1/oauth/test', OAUTH_HTTP_METHOD_GET);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch(OAuthException $e)
|
|
||||||
{
|
|
||||||
getLogger()->crit(OAuthProvider::reportProblem($e));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConsumer($provider)
|
|
||||||
{
|
|
||||||
// $consumer = new stdClass;
|
|
||||||
// $consumer->consumer_key = 'token';//$provider->consumer_key;
|
|
||||||
// $consumer->key_status = 0;
|
|
||||||
// $consumer->secret = 'secret';
|
|
||||||
|
|
||||||
// if($provider->consumer_key != $consumer->consumer_key)
|
|
||||||
// return OAUTH_CONSUMER_KEY_UNKNOWN;
|
|
||||||
// else if($consumer->key_status != 0) // 0 is active, 1 is throttled, 2 is blacklisted
|
|
||||||
// return OAUTH_CONSUMER_KEY_REFUSED;
|
|
||||||
|
|
||||||
$provider->consumer_secret = 'token'; //$consumer->secret;
|
|
||||||
return OAUTH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function checkTimestampAndNonce($provider)
|
|
||||||
{
|
|
||||||
return OAUTH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function tokenHandler($provider)
|
|
||||||
{
|
|
||||||
$provider->token_secret = 'token';
|
|
||||||
return OAUTH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateConsumerKeyAndSecret() {
|
|
||||||
$fp = fopen('/dev/urandom','rb');
|
|
||||||
$entropy = fread($fp, 32);
|
|
||||||
fclose($fp);
|
|
||||||
// in case /dev/urandom is reusing entropy from its pool, let's add a bit more entropy
|
|
||||||
$entropy .= uniqid(mt_rand(), true);
|
|
||||||
$hash = sha1($entropy); // sha1 gives us a 40-byte hash
|
|
||||||
// The first 30 bytes should be plenty for the consumer_key
|
|
||||||
// We use the last 10 for the shared secret
|
|
||||||
return array(substr($hash,0,30),substr($hash,30,10));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOAuthParameters()
|
|
||||||
{
|
|
||||||
// default null values
|
|
||||||
// $params = array('oauth_consumer_key' => 'token', 'oauth_token' => 'token', 'oauth_nonce' => null, 'oauth_timestamp' => null,
|
|
||||||
// 'oauth_signature_method' => null, 'oauth_signature' => null);
|
|
||||||
|
|
||||||
// fetch values from header
|
|
||||||
$headers = getallheaders();
|
|
||||||
foreach($headers as $name => $header)
|
|
||||||
{
|
|
||||||
if(stripos($name, 'authorization') === 0)
|
|
||||||
{
|
|
||||||
$parameters = explode(',', $header);
|
|
||||||
foreach($parameters as $parameter)
|
|
||||||
{
|
|
||||||
list($key, $value) = explode('=', $parameter);
|
|
||||||
if(strpos($key, 'oauth_') !== 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$params[$key] = urldecode(substr($value, 1, -1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override with values from GET
|
|
||||||
foreach($_GET as $key => $value)
|
|
||||||
{
|
|
||||||
if(strpos($key, 'oauth_') === 0)
|
|
||||||
$params[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ksort($params);
|
|
||||||
print_r($params);
|
|
||||||
return $params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initProvider()
|
|
||||||
{
|
|
||||||
if(!$this->provider)
|
|
||||||
$this->provider = new OAuthProvider($this->getOAuthParameters());
|
|
||||||
|
|
||||||
if(!$this->provider)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
154
src/libraries/models/Credential.php
Normal file
154
src/libraries/models/Credential.php
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<?php
|
||||||
|
class Credential
|
||||||
|
{
|
||||||
|
const typeUnauthorizedRequest = 'unauthorized_request';
|
||||||
|
const typeRequest = 'request';
|
||||||
|
const typeAccess = 'access';
|
||||||
|
|
||||||
|
const statusInactive = '0';
|
||||||
|
const statusActive = '1';
|
||||||
|
private $provider, $consumer;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->provider = new OAuthProvider($this->getOAuthParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($name, $permissions = array('read'))
|
||||||
|
{
|
||||||
|
$random = bin2hex($this->provider->generateToken(25));
|
||||||
|
$id = substr($random, 0, 30);
|
||||||
|
$params = array(
|
||||||
|
'name' => $name,
|
||||||
|
'client_secret' => substr($random, -10),
|
||||||
|
/*'user_token' => '',
|
||||||
|
'user_secret' => '',*/
|
||||||
|
'permissions' => $permissions,
|
||||||
|
'verifier' => substr($random, 30, 10),
|
||||||
|
'type' => self::typeUnauthorizedRequest,
|
||||||
|
'status' => self::statusActive
|
||||||
|
);
|
||||||
|
$res = getDb()->putCredential($id, $params);
|
||||||
|
if($res)
|
||||||
|
return $id;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addUserToken($id, $convertToAccessToken = false)
|
||||||
|
{
|
||||||
|
$random = bin2hex($this->provider->generateToken(20));
|
||||||
|
$params = array(
|
||||||
|
'user_token' => substr($random, 0, 30),
|
||||||
|
'user_secret' => substr($random, -10)
|
||||||
|
);
|
||||||
|
if($convertToAccessToken)
|
||||||
|
$params['type'] = self::typeAccess;
|
||||||
|
return getDb()->postCredential($id, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkRequest()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->provider->consumerHandler(array($this,'checkConsumer'));
|
||||||
|
$this->provider->timestampNonceHandler(array($this,'checkTimestampAndNonce'));
|
||||||
|
$this->provider->tokenHandler(array($this,'checkToken'));
|
||||||
|
$this->provider->setParam('__route__', null);
|
||||||
|
$this->provider->setRequestTokenPath('/v1/oauth/token/request'); // No token needed for this end point
|
||||||
|
$this->provider->checkOAuthRequest();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(OAuthException $e)
|
||||||
|
{
|
||||||
|
getLogger()->crit(OAuthProvider::reportProblem($e));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkConsumer($provider)
|
||||||
|
{
|
||||||
|
$consumer = $this->getConsumer($provider->consumer_key);
|
||||||
|
if(!$consumer)
|
||||||
|
{
|
||||||
|
getLogger()->warn(sprintf('Could not find consumer for key %s', $provider->consumer_key));
|
||||||
|
return OAUTH_CONSUMER_KEY_UNKNOWN;
|
||||||
|
}
|
||||||
|
else if($consumer['status'] != self::statusActive)
|
||||||
|
{
|
||||||
|
getLogger()->warn(sprintf('Consumer key %s refused', $provider->consumer_key));
|
||||||
|
return OAUTH_CONSUMER_KEY_REFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider->consumer_secret = $consumer['client_secret'];
|
||||||
|
return OAUTH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkTimestampAndNonce($provider)
|
||||||
|
{
|
||||||
|
// TODO check nonce in APC/Memcached using EpiCache.
|
||||||
|
return OAUTH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkToken($provider)
|
||||||
|
{
|
||||||
|
$consumer = $this->getConsumer($provider->consumer_key);
|
||||||
|
if(!$consumer)
|
||||||
|
{
|
||||||
|
getLogger()->warn(sprintf('Could not find consumer for key %s', $provider->consumer_key));
|
||||||
|
return OAUTH_CONSUMER_KEY_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider->token_secret = $consumer['user_secret'];
|
||||||
|
return OAUTH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConsumer($consumerKey)
|
||||||
|
{
|
||||||
|
if(!$this->consumer)
|
||||||
|
$this->consumer = getDb()->getCredential($consumerKey);
|
||||||
|
|
||||||
|
return $this->consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOAuthParameters()
|
||||||
|
{
|
||||||
|
$params = array();
|
||||||
|
// fetch values from header
|
||||||
|
$headers = getallheaders();
|
||||||
|
foreach($headers as $name => $header)
|
||||||
|
{
|
||||||
|
if(stripos($name, 'authorization') === 0)
|
||||||
|
{
|
||||||
|
$parameters = explode(',', $header);
|
||||||
|
foreach($parameters as $parameter)
|
||||||
|
{
|
||||||
|
list($key, $value) = explode('=', $parameter);
|
||||||
|
if(strpos($key, 'oauth_') !== 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$params[$key] = urldecode(substr($value, 1, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override with values from GET
|
||||||
|
foreach($_GET as $key => $value)
|
||||||
|
{
|
||||||
|
if(strpos($key, 'oauth_') === 0)
|
||||||
|
$params[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($params);
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCredential()
|
||||||
|
{
|
||||||
|
static $credential;
|
||||||
|
if(!$credential)
|
||||||
|
$credential = new Credential;
|
||||||
|
|
||||||
|
return $credential;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ getRoute()->get('/tags', array('TagController', 'tags'));
|
||||||
|
|
||||||
// oauth request token
|
// oauth request token
|
||||||
getRoute()->get('/v[1]/oauth/authorize', array('OAuthController', 'authorize'));
|
getRoute()->get('/v[1]/oauth/authorize', array('OAuthController', 'authorize'));
|
||||||
|
getRoute()->post('/v[1]/oauth/authorize', array('OAuthController', 'authorizePost'));
|
||||||
getRoute()->post('/v[1]/oauth/token/access', array('OAuthController', 'tokenAccess'));
|
getRoute()->post('/v[1]/oauth/token/access', array('OAuthController', 'tokenAccess'));
|
||||||
getRoute()->post('/v[1]/oauth/token/request', array('OAuthController', 'tokenRequest'));
|
getRoute()->post('/v[1]/oauth/token/request', array('OAuthController', 'tokenRequest'));
|
||||||
getRoute()->get('/v[1]/oauth/test', array('OAuthController', 'test'));
|
getRoute()->get('/v[1]/oauth/test', array('OAuthController', 'test'));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue