From db2f09d46ce2f3a46be1b6f6e031492966242025 Mon Sep 17 00:00:00 2001 From: Christian Weiske Date: Mon, 18 Aug 2014 23:54:32 +0200 Subject: oauth dance works --- controller/accesscontroller.php | 145 ------------------------------ controller/apicontroller.php | 122 ++++++++++++++++--------- controller/oauthcontroller.php | 191 +++++++++++++++++++++++++++++----------- 3 files changed, 220 insertions(+), 238 deletions(-) delete mode 100644 controller/accesscontroller.php (limited to 'controller') 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); } -- cgit v1.2.3