From: Christian Weiske Date: Mon, 18 Aug 2014 21:54:32 +0000 (+0200) Subject: oauth dance works X-Git-Tag: v0.1.0~20 X-Git-Url: https://git.cweiske.de/grauphel.git/commitdiff_plain/db2f09d46ce2f3a46be1b6f6e031492966242025 oauth dance works --- diff --git a/appinfo/application.php b/appinfo/application.php index ecee129..7a1691d 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -1,6 +1,7 @@ getContainer(); + $container->registerService( + 'Session', + function($c) { + return $c->query('ServerContainer')->getUserSession(); + } + ); + /** * Controllers */ $container->registerService( - 'ApiController', + 'ApiController', function($c) { + Dependencies::get()->urlGen + = $c->query('ServerContainer')->getURLGenerator(); return new \OCA\Grauphel\Controller\ApiController( $c->query('AppName'), $c->query('Request') @@ -23,8 +33,10 @@ class Application extends App } ); $container->registerService( - 'AccessController', + 'AccessController', function($c) { + Dependencies::get()->urlGen + = $c->query('ServerContainer')->getURLGenerator(); return new \OCA\Grauphel\Controller\AccessController( $c->query('AppName'), $c->query('Request') @@ -32,11 +44,14 @@ class Application extends App } ); $container->registerService( - 'OAuthController', + 'OauthController', function($c) { - return new \OCA\Grauphel\Controller\OAuthController( + Dependencies::get()->urlGen + = $c->query('ServerContainer')->getURLGenerator(); + return new \OCA\Grauphel\Controller\OauthController( $c->query('AppName'), - $c->query('Request') + $c->query('Request'), + $c->query('Session')->getUser() ); } ); diff --git a/appinfo/routes.php b/appinfo/routes.php index da62dc9..5567670 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -11,16 +11,11 @@ $application->registerRoutes( 'name' => 'access#test', ), - array( - 'url' => '/authorize', - 'name' => 'access#authorize', - 'verb' => 'GET', - ),/* array( 'url' => '/authorize', 'name' => 'access#authorize', 'verb' => 'POST', - ),*/ + ), array( 'url' => '/login', 'name' => 'access#login', @@ -37,6 +32,11 @@ $application->registerRoutes( 'name' => 'oauth#authorize', 'verb' => 'GET', ), + array( + 'url' => '/oauth/confirm', + 'name' => 'oauth#confirm', + 'verb' => 'POST', + ), array( 'url' => '/oauth/request_token', 'name' => 'oauth#requestToken', diff --git a/controller/accesscontroller.php b/controller/accesscontroller.php deleted file mode 100644 index 68789b7..0000000 --- a/controller/accesscontroller.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @copyright 2014 Christian Weiske - * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 - * @link http://cweiske.de/grauphel.htm - */ -namespace OCA\Grauphel\Controller; -use \OCP\AppFramework\Controller; - -/** - * Login and authorization handling - * - * @category Tools - * @package Grauphel - * @author Christian Weiske - * @copyright 2014 Christian Weiske - * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 - * @version Release: @package_version@ - * @link http://cweiske.de/grauphel.htm - */ -class AccessController extends Controller -{ - public function login($returnUrl = null) - { - $returnUrl = $this->loadReturnUrl($returnUrl); - - if (isset($_POST['user']) && trim($_POST['user']) != '') { - $this->deps->frontend->setUser(trim($_POST['user'])); - header('Location: ' . $returnUrl); - exit(0); - } - - $hFormUrl = htmlspecialchars( - $this->deps->urlGen->addParams( - $this->deps->urlGen->accessLogin(), - array('returnurl' => $returnUrl) - ) - ); - //FIXME: do some real login - header('HTTP/1.0 200 OK'); - - echo << - - grauphel login - - -
-

- Log into grauphel: -

- - -
- - - -HTM; - exit(0); - } - - public function authorize($returnUrl = null) - { - var_dump('asd');die(); - $returnUrl = $this->loadReturnUrl($returnUrl); - - if (isset($_POST['auth'])) { - if ($_POST['auth'] == 'ok') { - $this->deps->frontend->setAuth(true); - } else if ($_POST['auth'] == 'cancel') { - $this->deps->frontend->setAuth(false); - } - header('Location: ' . $returnUrl); - exit(0); - } - - header('HTTP/1.0 200 OK'); - $hFormUrl = htmlspecialchars( - $this->deps->urlGen->addParams( - $this->deps->urlGen->accessAuthorize(), - array('returnurl' => $returnUrl) - ) - ); - - echo << - - grauphel authorization - - -
-

- Shall application FIXME get full access to the notes? -

- - - - -HTM; - exit(0); - } - - protected function loadReturnUrl($returnUrl = null) - { - if ($returnUrl === null) { - if (isset($_GET['returnurl'])) { - $returnUrl = $_GET['returnurl']; - } else { - $returnUrl = $this->deps->urlGen->index(); - } - } - return $returnUrl; - } - - /** - * @NoAdminRequired - * @NoCSRFRequired - * @PublicPage - */ - public function test() - { - var_dump('asd');die(); - $this->registerResponder('xml', function($value) { - return new XMLResponse($value); - }); - return array('foo' => 'bar'); - } -} -?> diff --git a/controller/apicontroller.php b/controller/apicontroller.php index 5e743e0..8ce8997 100644 --- a/controller/apicontroller.php +++ b/controller/apicontroller.php @@ -12,7 +12,12 @@ * @link http://cweiske.de/grauphel.htm */ namespace OCA\Grauphel\Controller; + use \OCP\AppFramework\Controller; +use \OCP\AppFramework\Http\JSONResponse; + +use \OCA\Grauphel\Lib\OAuth; +use \OCA\Grauphel\Lib\Dependencies; /** * Tomboy's REST API @@ -29,26 +34,34 @@ class ApiController extends Controller { /** * /api/1.0 + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ public function index() { - var_dump('asd');die(); + $deps = Dependencies::get(); $authenticated = false; $oauth = new OAuth(); - $oauth->setDeps($this->deps); - $urlGen = $this->deps->urlGen; + $oauth->setDeps($deps); + $urlGen = $deps->urlGen; try { $provider = new \OAuthProvider(); $oauth->registerHandler($provider) ->registerAccessTokenHandler($provider); - $provider->checkOAuthRequest($urlGen->fullPath()); + $provider->checkOAuthRequest( + $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.api.index') + ) + ); $authenticated = true; - $token = $this->deps->tokens->load('access', $provider->token); + $token = $deps->tokens->load('access', $provider->token); $username = $token->user; - } catch (OAuth_Exception $e) { - $this->deps->renderer->errorOut($e->getMessage()); + } catch (\OAuth_Exception $e) { + $deps->renderer->errorOut($e->getMessage()); } catch (\OAuthException $e) { if ($e->getCode() != OAUTH_PARAMETER_ABSENT) { $oauth->error($e); @@ -56,34 +69,50 @@ class ApiController extends Controller } $data = array( - 'oauth_request_token_url' => $urlGen->oauthRequestToken(), - 'oauth_authorize_url' => $urlGen->oauthAuthorize(), - 'oauth_access_token_url' => $urlGen->oauthAccessToken(), + 'oauth_request_token_url' => $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.oauth.requestToken') + ), + 'oauth_authorize_url' => $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.oauth.authorize') + ), + 'oauth_access_token_url' => $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.oauth.accessToken') + ), 'api-version' => '1.0', ); if ($authenticated) { $data['user-ref'] = array( - 'api-ref' => $urlGen->user($username), - 'href' => $urlGen->userHtml($username), + 'api-ref' => $urlGen->getAbsoluteURL( + $urlGen->linkToRoute( + 'grauphel.api.user', array('user' => $username) + ) + ), + 'href' => null,//FIXME ); } - $this->deps->renderer->sendJson($data); + return new JSONResponse($data); + $deps->renderer->sendJson($data); } /** * GET /api/1.0/$user/notes/$noteguid + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ public function note() { - $username = $this->deps->urlGen->loadUsername(); - $guid = $this->deps->urlGen->loadGuid(); - $oauth = new OAuth(); - $oauth->setDeps($this->deps); - $oauth->verifyOAuthUser($username, $this->deps->urlGen->note($username, $guid)); - - $note = $this->deps->notes->load($username, $guid, false); + $deps = Dependencies::get(); + $username = $deps->urlGen->loadUsername(); + $guid = $deps->urlGen->loadGuid(); + $oauth = new \OAuth(); + $oauth->setDeps($deps); + $oauth->verifyOAuthUser($username, $deps->urlGen->note($username, $guid)); + + $note = $deps->notes->load($username, $guid, false); if ($note === null) { header('HTTP/1.0 404 Not Found'); header('Content-type: text/plain'); @@ -92,20 +121,25 @@ class ApiController extends Controller } $data = array('note' => array($note)); - $this->deps->renderer->sendJson($data); + $deps->renderer->sendJson($data); } /** * GET|PUT /api/1.0/$user/notes + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ public function notes() { - $username = $this->deps->urlGen->loadUsername(); - $oauth = new OAuth(); - $oauth->setDeps($this->deps); - $oauth->verifyOAuthUser($username, $this->deps->urlGen->notes($username)); + $deps = Dependencies::get(); + $username = $deps->urlGen->loadUsername(); + $oauth = new \OAuth(); + $oauth->setDeps($deps); + $oauth->verifyOAuthUser($username, $deps->urlGen->notes($username)); - $syncdata = $this->deps->notes->loadSyncData($username); + $syncdata = $deps->notes->loadSyncData($username); $this->handleNoteSave($username, $syncdata); @@ -115,9 +149,9 @@ class ApiController extends Controller } if (isset($_GET['include_notes']) && $_GET['include_notes']) { - $notes = $this->deps->notes->loadNotesFull($username, $since); + $notes = $deps->notes->loadNotesFull($username, $since); } else { - $notes = $this->deps->notes->loadNotesOverview($username, $since); + $notes = $deps->notes->loadNotesOverview($username, $since); } //work around bug https://bugzilla.gnome.org/show_bug.cgi?id=734313 @@ -131,7 +165,7 @@ class ApiController extends Controller 'latest-sync-revision' => $syncdata->latestSyncRevision, 'notes' => $notes, ); - $this->deps->renderer->sendJson($data); + $deps->renderer->sendJson($data); } protected function handleNoteSave($username, $syncdata) @@ -167,47 +201,53 @@ class ApiController extends Controller } //update + $deps = Dependencies::get(); ++$syncdata->latestSyncRevision; foreach ($putObj->{'note-changes'} as $noteUpdate) { - $note = $this->deps->notes->load($username, $noteUpdate->guid); + $note = $deps->notes->load($username, $noteUpdate->guid); if (isset($noteUpdate->command) && $noteUpdate->command == 'delete') { - $this->deps->notes->delete($username, $noteUpdate->guid); + $deps->notes->delete($username, $noteUpdate->guid); } else { - $this->deps->notes->update( + $deps->notes->update( $note, $noteUpdate, $syncdata->latestSyncRevision ); - $this->deps->notes->save($username, $note); + $deps->notes->save($username, $note); } } - $this->deps->notes->saveSyncData($username, $syncdata); + $deps->notes->saveSyncData($username, $syncdata); } /** * GET /api/1.0/$user + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ public function user() { - $username = $this->deps->urlGen->loadUsername(); + $deps = Dependencies::get(); + $username = $deps->urlGen->loadUsername(); - $oauth = new OAuth(); - $oauth->setDeps($this->deps); - $oauth->verifyOAuthUser($username, $this->deps->urlGen->user($username)); + $oauth = new \OAuth(); + $oauth->setDeps($deps); + $oauth->verifyOAuthUser($username, $deps->urlGen->user($username)); - $syncdata = $this->deps->notes->loadSyncData($username); + $syncdata = $deps->notes->loadSyncData($username); $data = array( 'user-name' => $username, 'first-name' => null, 'last-name' => null, 'notes-ref' => array( - 'api-ref' => $this->deps->urlGen->notes($username), + 'api-ref' => $deps->urlGen->notes($username), 'href' => null, ), 'latest-sync-revision' => $syncdata->latestSyncRevision, 'current-sync-guid' => $syncdata->currentSyncGuid, ); - $this->deps->renderer->sendJson($data); + $deps->renderer->sendJson($data); } } ?> diff --git a/controller/oauthcontroller.php b/controller/oauthcontroller.php index cb20832..1dfdec9 100644 --- a/controller/oauthcontroller.php +++ b/controller/oauthcontroller.php @@ -12,7 +12,20 @@ * @link http://cweiske.de/grauphel.htm */ namespace OCA\Grauphel\Controller; + use \OCP\AppFramework\Controller; +use \OCP\AppFramework\Http; +use \OCP\AppFramework\Http\JSONResponse; +use \OCP\AppFramework\Http\RedirectResponse; +use \OCP\AppFramework\Http\TemplateResponse; + +use \OCA\Grauphel\Lib\Token; +use \OCA\Grauphel\Lib\OAuth; +use \OCA\Grauphel\Lib\Dependencies; +use \OCA\Grauphel\Lib\Response\ErrorResponse; +use \OCA\Grauphel\Lib\Response\FormResponse; +use \OCA\Grauphel\Lib\OAuthException; +use \OCA\Grauphel\Lib\UrlHelper; /** * OAuth handling @@ -25,38 +38,66 @@ use \OCP\AppFramework\Controller; * @version Release: @package_version@ * @link http://cweiske.de/grauphel.htm */ -class OAuthController extends Controller +class OauthController extends Controller { + protected $user; + + /** + * constructor of the controller + * + * @param string $appName Name of the app + * @param IRequest $request Instance of the request + */ + public function __construct($appName, \OCP\IRequest $request, $user) + { + parent::__construct($appName, $request); + $this->user = $user; + $this->deps = Dependencies::get(); + + //default http header: we assume something is broken + header('HTTP/1.0 500 Internal Server Error'); + } + /** * Handle out an access token after verifying the verification token * OAuth step 3 of 3 + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ - public function accessTokenAction() + public function accessToken() { $oauth = new OAuth(); $oauth->setDeps($this->deps); + $urlGen = $this->deps->urlGen; try { $provider = new \OAuthProvider(); $oauth->registerHandler($provider) ->registerVerificationTokenHandler($provider); - $provider->checkOAuthRequest($this->deps->urlGen->oauthAccessToken()); + $provider->checkOAuthRequest( + $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.oauth.accessToken') + ) + ); $token = $this->deps->tokens->loadAndDelete('verify', $provider->token); - $newToken = new OAuth_Token('access'); + $newToken = new Token('access'); $newToken->tokenKey = 'a' . bin2hex($provider->generateToken(8)); $newToken->secret = 's' . bin2hex($provider->generateToken(8)); $newToken->user = $token->user; $this->deps->tokens->store($newToken); - $this->deps->renderer->sendFormData( + return new FormResponse( array( 'oauth_token' => $newToken->tokenKey, 'oauth_token_secret' => $newToken->secret, ) ); - exit(0); + } catch (OAuthException $e) { + return new ErrorResponse($e->getMessage()); } catch (\OAuthException $e) { $oauth->error($e); } @@ -65,111 +106,157 @@ class OAuthController extends Controller /** * Log the user in and let him authorize that the app may access notes * OAuth step 2 of 3 + * + * Page is not public and thus requires owncloud login + * + * @NoAdminRequired + * @NoCSRFRequired */ - public function authorizeAction() + public function authorize() { - $oauth = new OAuth(); - $oauth->setDeps($this->deps); - - if (!isset($_REQUEST['oauth_token'])) { - $this->deps->renderer->errorOut('oauth_token missing'); - } - if (!$oauth->validateToken($_REQUEST['oauth_token'])) { - $this->deps->renderer->errorOut('Invalid token string'); - } - - $reqToken = $_REQUEST['oauth_token']; - - try { - $token = $this->deps->tokens->load('temp', $reqToken); - } catch (OAuth_Exception $e) { - $this->deps->renderer->errorOut($e->getMessage()); + $token = $this->verifyRequestToken(); + if (!$token instanceof Token) { + return $token; } - $authState = $this->deps->frontend->doAuthorize( - $this->deps->urlGen->current() + $res = new TemplateResponse('grauphel', 'oauthAuthorize'); + $res->setParams( + array( + 'oauth_token' => $token->tokenKey, + 'formaction' => $this->deps->urlGen->linkToRoute( + 'grauphel.oauth.confirm' + ), + ) ); - if ($authState === null) { - //this should not happen; doAuthorize() must block - exit(1); - } + return $res; + } + + /** + * User confirms or declines the authorization request + * OAuth step 2.5 of 3 + * + * @NoAdminRequired + */ + public function confirm() + { + $token = $this->verifyRequestToken(); + $oauth = new OAuth(); + $oauth->setDeps($this->deps); try { - $token = $this->deps->tokens->loadAndDelete('temp', $reqToken); - } catch (OAuth_Exception $e) { - $this->deps->renderer->errorOut($e->getMessage()); + $token = $this->deps->tokens->loadAndDelete('temp', $token->tokenKey); + } catch (OAuthException $e) { + return new ErrorResponse($e->getMessage()); } + $authState = isset($_POST['auth']) && $_POST['auth'] == 'ok'; if ($authState === false) { //user declined //http://wiki.oauth.net/w/page/12238543/ProblemReporting - $callback = $this->deps->urlGen->addParams( - $token->callback, - array( - 'oauth_token' => $token->tokenKey, - 'oauth_problem' => 'permission_denied', + $res = new RedirectResponse( + UrlHelper::addParams( + $token->callback, + array( + 'oauth_token' => $token->tokenKey, + 'oauth_problem' => 'permission_denied', + ) ) ); - header('Location: ' . $callback, true, 302); - exit(0); + $res->setStatus(Http::STATUS_SEE_OTHER); + return $res; } //the user is logged in and authorized $provider = new \OAuthProvider(); - $newToken = new OAuth_Token('verify'); + $newToken = new Token('verify'); $newToken->tokenKey = $token->tokenKey; $newToken->secret = $token->secret; $newToken->verifier = 'v' . bin2hex($provider->generateToken(8)); - $newToken->user = $this->deps->frontend->getUser(); + $newToken->user = $this->user->getUID(); $this->deps->tokens->store($newToken); //redirect //FIXME: if no callback is given, show the token to the user - $callback = $this->deps->urlGen->addParams( - $token->callback, - array( - 'oauth_token' => $newToken->tokenKey, - 'oauth_verifier' => $newToken->verifier + $res = new RedirectResponse( + UrlHelper::addParams( + $token->callback, + array( + 'oauth_token' => $newToken->tokenKey, + 'oauth_verifier' => $newToken->verifier + ) ) ); + $res->setStatus(Http::STATUS_SEE_OTHER); + return $res; + } + + protected function verifyRequestToken() + { + if (!isset($_REQUEST['oauth_token'])) { + return new ErrorResponse('oauth_token missing'); + } - header('Location: ' . $callback, true, 302); - exit(); + $oauth = new OAuth(); + $oauth->setDeps($this->deps); + if (!$oauth->validateToken($_REQUEST['oauth_token'])) { + return new ErrorResponse('Invalid token string'); + } + + $reqToken = $_REQUEST['oauth_token']; + + try { + $token = $this->deps->tokens->load('temp', $reqToken); + } catch (OAuthException $e) { + return new ErrorResponse($e->getMessage()); + } + + return $token; } /** * Create and return a request token. * OAuth step 1 of 3 + * + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage */ - public function requestTokenAction() + public function requestToken() { $oauth = new OAuth(); $oauth->setDeps($this->deps); + $urlGen = $this->deps->urlGen; try { $provider = new \OAuthProvider(); $oauth->registerHandler($provider); $provider->isRequestTokenEndpoint(true); - $provider->checkOAuthRequest($this->deps->urlGen->oauthRequestToken()); + $provider->checkOAuthRequest( + $urlGen->getAbsoluteURL( + $urlGen->linkToRoute('grauphel.oauth.requestToken') + ) + ); //store token + callback URI for later - $token = new OAuth_Token('temp'); + $token = new Token('temp'); $token->tokenKey = 'r' . bin2hex($provider->generateToken(8)); $token->secret = 's' . bin2hex($provider->generateToken(8)); $token->callback = $provider->callback; $this->deps->tokens->store($token); - $this->deps->renderer->sendFormData( + return new FormResponse( array( 'oauth_token' => $token->tokenKey, 'oauth_token_secret' => $token->secret, 'oauth_callback_confirmed' => 'TRUE' ) ); + } catch (OAuthException $e) { + return new ErrorResponse($e->getMessage()); } catch (\OAuthException $e) { $oauth->error($e); } diff --git a/lib/dependencies.php b/lib/dependencies.php new file mode 100644 index 0000000..bad48b2 --- /dev/null +++ b/lib/dependencies.php @@ -0,0 +1,75 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * Object container + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class Dependencies +{ + /** + * @var Frontend\Default + */ + public $frontend; + + /** + * @var Note\Storage + */ + public $noteStorage; + + /** + * @var OAuth\Storage + */ + public $oauthStorage; + + /** + * @var IURLGenerator + */ + public $urlGen; + + protected static $instance; + + public static function get() + { + if (self::$instance !== null) { + return self::$instance; + } + $deps = new self(); + /* + $deps->notes = new Note_Storage_Flatfile(); + $deps->notes->setDataDir($dataDir); + $deps->notes->setDeps($deps); + + $deps->urlGen = new UrlGen_Pretty(); + $deps->urlGen->setDeps($deps); + /* + $deps->frontend = new Frontend_Default(); + $deps->frontend->setDeps($deps); + */ + + $deps->tokens = new TokenStorage(); + + self::$instance = $deps; + return self::$instance; + } +} +?> diff --git a/lib/oauth.php b/lib/oauth.php new file mode 100644 index 0000000..7cfd4e0 --- /dev/null +++ b/lib/oauth.php @@ -0,0 +1,135 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * Storage base class that implements note updating + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class OAuth +{ + /** + * Token data store + * + * @var Token_Storage + */ + protected $tokens; + + public function setDeps(Dependencies $deps) + { + $this->tokens = $deps->tokens; + } + + /** + * Register callbacks for the oauth dance. + */ + public function registerHandler(\OAuthProvider $provider) + { + $provider->consumerHandler(array($this, 'lookupConsumer')); + $provider->timestampNonceHandler(array($this, 'timestampNonceChecker')); + return $this; + } + + public function registerVerificationTokenHandler(\OAuthProvider $provider) + { + $provider->tokenHandler(array($this, 'verifyTokenHandler')); + return $this; + } + + public function registerAccessTokenHandler(\OAuthProvider $provider) + { + $provider->tokenHandler(array($this, 'accessTokenHandler')); + return $this; + } + + public function validateToken($tokenKey) + { + return (bool) preg_match('#^[a-z0-9]+$#', $tokenKey); + } + + public function lookupConsumer(\OAuthProvider $provider) + { + //tomboy assumes secret==key=="anyone" + $provider->consumer_secret = $provider->consumer_key;//'anyone'; + $provider->addRequiredParameter('oauth_callback'); + + return OAUTH_OK; + } + + public function timestampNonceChecker(\OAuthProvider $provider) + { + //var_dump($provider->nonce, $provider->timestamp); + //OAUTH_BAD_NONCE + //OAUTH_BAD_TIMESTAMP + return OAUTH_OK; + } + + public function verifyTokenHandler(\OAuthProvider $provider) + { + $token = $this->tokens->load('verify', $provider->token); + if ($provider->verifier == '') { + return OAUTH_VERIFIER_INVALID; + } + if ($provider->verifier != $token->verifier) { + return OAUTH_VERIFIER_INVALID; + } + + $provider->token_secret = $token->secret; + return OAUTH_OK; + } + + public function accessTokenHandler(\OAuthProvider $provider) + { + $token = $this->tokens->load('access', $provider->token); + $provider->token_secret = $token->secret; + return OAUTH_OK; + } + + public function verifyOAuthUser($username, $url) + { + try { + $provider = new \OAuthProvider(); + $this->registerHandler($provider); + $this->registerAccessTokenHandler($provider); + //do not use "user" in signature + $provider->setParam('user', null); + + $provider->checkOAuthRequest($url); + + $token = $this->tokens->load('access', $provider->token); + if ($token->user != $username) { + errorOut('Invalid user'); + } + } catch (\OAuthException $e) { + $this->error($e); + } + } + + public function error(\OAuthException $e) + { + header('HTTP/1.0 400 Bad Request'); + //header('Content-type: application/x-www-form-urlencoded'); + echo \OAuthProvider::reportProblem($e); + //var_dump($e); + exit(1); + } +} +?> diff --git a/lib/oauthexception.php b/lib/oauthexception.php new file mode 100644 index 0000000..d1b26f8 --- /dev/null +++ b/lib/oauthexception.php @@ -0,0 +1,30 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * OAuth error + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class OAuthException extends \Exception +{ +} +?> diff --git a/lib/response/errorresponse.php b/lib/response/errorresponse.php new file mode 100644 index 0000000..b72224f --- /dev/null +++ b/lib/response/errorresponse.php @@ -0,0 +1,20 @@ +setStatus(\OCP\AppFramework\Http::STATUS_BAD_REQUEST); + $this->addHeader('Content-Type', 'text/plain; charset=utf-8'); + $this->error = $error; + } + + public function render() + { + return $this->error . "\n"; + } +} +?> diff --git a/lib/response/formresponse.php b/lib/response/formresponse.php new file mode 100644 index 0000000..e7ce33d --- /dev/null +++ b/lib/response/formresponse.php @@ -0,0 +1,20 @@ +setStatus(\OCP\AppFramework\Http::STATUS_OK); + $this->addHeader('Content-Type', 'application/x-www-form-urlencoded'); + $this->data = $data; + } + + public function render() + { + return http_build_query($this->data, null, '&'); + } +} +?> diff --git a/lib/syncdata.php b/lib/syncdata.php new file mode 100644 index 0000000..b8cf873 --- /dev/null +++ b/lib/syncdata.php @@ -0,0 +1,62 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * Synchronization data model + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class SyncData +{ + /** + * The latest sync revision from Tomboy, given from last PUT + * of a note from Tomboy. + * Give a -1 here if you have not synced with Tomboy yet., + * + * @var integer + */ + public $latestSyncRevision; + + /** + * A uuid generated by the sync application. + * It should change only if the user decides to clear their + * sync history from the server and start over + * with an empty note set. + * + * @var string + */ + public $currentSyncGuid; + + /** + * Initialize the variables to represent the data of a user + * that never synced + * + * @param string $username Name of user + * + * @return void + */ + public function initNew($username) + { + $this->latestSyncRevision = -1; + $this->currentSyncGuid = uniqid($username . '-', true); + } +} +?> diff --git a/lib/token.php b/lib/token.php new file mode 100644 index 0000000..ebb0783 --- /dev/null +++ b/lib/token.php @@ -0,0 +1,77 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * OAuth token with some additional data + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class Token +{ + /** + * One of: temp, access, verify + * + * @var string + */ + public $type; + + /** + * Actual random token string + * + * @var string + */ + public $tokenKey; + + /** + * Matching secret for the token string + * + * @var string + */ + public $secret; + + /** + * User name for which the token is valid + * + * @var string + */ + public $user; + + /** + * Verification string. + * Only used when $type == 'verify' + * + * @var string + */ + public $verifier; + + /** + * Callback URL for temp tokens + * + * @var string + */ + public $callback; + + public function __construct($type = null) + { + $this->type = $type; + } +} +?> diff --git a/lib/tokenstorage.php b/lib/tokenstorage.php new file mode 100644 index 0000000..c40ab33 --- /dev/null +++ b/lib/tokenstorage.php @@ -0,0 +1,121 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * Token store + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class TokenStorage +{ + /** + * Store the given token + * + * @param OAuth_Token $token Token object to store + * + * @return void + */ + public function store(OAuth_Token $token) + { + \OC_DB::executeAudited( + 'INSERT INTO `*PREFIX*grauphel_oauth_tokens`' + . '(`token_user`, `token_type`, `token_key`, `token_secret`, `token_verifier`, `token_callback`)' + . ' VALUES(?, ?, ?, ?, ?, ?)', + array( + $token->user, + $token->type, + $token->tokenKey, + (string) $token->secret, + (string) $token->verifier, + (string) $token->callback + ) + ); + } + + /** + * Load the token and destroy it. + * + * @param string $type Token type: temp, access, verify + * @param string $tokenKey Random token string to load + * + * @return OAuth_Token Stored token + * + * @throws OAuth_Exception When token does not exist + */ + public function loadAndDelete($type, $tokenKey) + { + try { + $token = $this->load($type, $tokenKey); + \OC_DB::executeAudited( + 'DELETE FROM `*PREFIX*grauphel_oauth_tokens`' + . ' WHERE `token_key` = ? AND `token_type` = ?', + array($tokenKey, $type) + ); + return $token; + } catch (OAuthException $e) { + throw $e; + } + } + + + /** + * Load the token. + * + * @param string $type Token type: temp, access, verify + * @param string $tokenKey Random token string to load + * + * @return OAuth_Token Stored token + * + * @throws OAuth_Exception When token does not exist or + */ + public function load($type, $tokenKey) + { + $tokenRow = \OC_DB::executeAudited( + 'SELECT * FROM `*PREFIX*grauphel_oauth_tokens`' + . ' WHERE `token_key` = ? AND `token_type` = ?', + array($tokenKey, $type) + )->fetchRow(); + + if ($tokenRow === false) { + throw new OAuthException('Unknown token: ' . $type . ' / ' . $tokenKey); + } + + $token = $this->fromDb($tokenRow); + if ($token->tokenKey != $tokenKey) { + throw new OAuthException('Invalid token'); + } + + return $token; + } + + protected function fromDb($tokenRow) + { + $token = new Token(); + $token->type = $tokenRow['token_user']; + $token->tokenKey = $tokenRow['token_key']; + $token->secret = $tokenRow['token_secret']; + $token->user = $tokenRow['token_user']; + $token->verifier = $tokenRow['token_verifier']; + $token->callback = $tokenRow['token_callback']; + return $token; + } +} +?> diff --git a/lib/urlhelper.php b/lib/urlhelper.php new file mode 100644 index 0000000..e0de8f9 --- /dev/null +++ b/lib/urlhelper.php @@ -0,0 +1,41 @@ + + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @link http://cweiske.de/grauphel.htm + */ +namespace OCA\Grauphel\Lib; + +/** + * URL helper methods + * + * @category Tools + * @package Grauphel + * @author Christian Weiske + * @copyright 2014 Christian Weiske + * @license http://www.gnu.org/licenses/agpl.html GNU AGPL v3 + * @version Release: @package_version@ + * @link http://cweiske.de/grauphel.htm + */ +class UrlHelper +{ + public static function addParams($url, $arParams) + { + $parts = array(); + foreach($arParams as $key => $val) { + if ($val != '') { + $parts[] = urlencode($key) . '=' . urlencode($val); + } + } + $sep = (strpos($url, '?') !== false) ? '&' : '?'; + return $url . $sep . implode('&', $parts); + } +} +?> diff --git a/templates/oauthAuthorize.php b/templates/oauthAuthorize.php new file mode 100644 index 0000000..5d4023b --- /dev/null +++ b/templates/oauthAuthorize.php @@ -0,0 +1,9 @@ + + + +

+ Shall application FIXME get full access to the notes? +

+ + +