oauth dance works
authorChristian Weiske <cweiske@cweiske.de>
Mon, 18 Aug 2014 21:54:32 +0000 (23:54 +0200)
committerChristian Weiske <cweiske@cweiske.de>
Mon, 18 Aug 2014 21:54:32 +0000 (23:54 +0200)
15 files changed:
appinfo/application.php
appinfo/routes.php
controller/accesscontroller.php [deleted file]
controller/apicontroller.php
controller/oauthcontroller.php
lib/dependencies.php [new file with mode: 0644]
lib/oauth.php [new file with mode: 0644]
lib/oauthexception.php [new file with mode: 0644]
lib/response/errorresponse.php [new file with mode: 0644]
lib/response/formresponse.php [new file with mode: 0644]
lib/syncdata.php [new file with mode: 0644]
lib/token.php [new file with mode: 0644]
lib/tokenstorage.php [new file with mode: 0644]
lib/urlhelper.php [new file with mode: 0644]
templates/oauthAuthorize.php [new file with mode: 0644]

index ecee129..7a1691d 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 namespace OCA\Grauphel\AppInfo;
 use \OCP\AppFramework\App;
+use \OCA\Grauphel\Lib\Dependencies;
 
 class Application extends App
 {
@@ -10,12 +11,21 @@ class Application extends App
 
         $container = $this->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()
                 );
             }
         );
index da62dc9..5567670 100644 (file)
@@ -14,13 +14,8 @@ $application->registerRoutes(
             array(
                 'url'  => '/authorize',
                 'name' => 'access#authorize',
-                'verb' => 'GET',
-            ),/*
-            array(
-                'url'  => '/authorize',
-                'name' => 'access#authorize',
                 'verb' => 'POST',
-                ),*/
+                ),
             array(
                 'url'  => '/login',
                 'name' => 'access#login',
@@ -38,6 +33,11 @@ $application->registerRoutes(
                 'verb' => 'GET',
             ),
             array(
+                'url'  => '/oauth/confirm',
+                'name' => 'oauth#confirm',
+                'verb' => 'POST',
+            ),
+            array(
                 'url'  => '/oauth/request_token',
                 'name' => 'oauth#requestToken',
                 'verb' => 'POST',
diff --git a/controller/accesscontroller.php b/controller/accesscontroller.php
deleted file mode 100644 (file)
index 68789b7..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-/**
- * Part of grauphel
- *
- * PHP version 5
- *
- * @category  Tools
- * @package   Grauphel
- * @author    Christian Weiske <cweiske@cweiske.de>
- * @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 <cweiske@cweiske.de>
- * @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 <<<HTM
-<html>
- <head>
-  <title>grauphel login</title>
- </head>
- <body>
-  <form method="post" action="$hFormUrl">
-   <p>
-    Log into <em>grauphel</em>:
-   </p>
-   <label>
-    User name:
-    <input id="user" type="text" name="user" size="20" value=""/>
-   </label>
-   <input type="submit" value="Login" />
-  </form>
-  <script type="text/javascript">
-//FIXME
-/*
-document.getElementById('user').value = 'cweiske';
-document.forms[0].submit();
-/**/
-  </script>
- </body>
-</html>
-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 <<<HTM
-<html>
- <head>
-  <title>grauphel authorization</title>
- </head>
- <body>
-  <form method="post" action="$hFormUrl">
-   <p>
-    Shall application FIXME get full access to the notes?
-   </p>
-   <button type="submit" name="auth" value="ok">Yes, authorize</button>
-   <button type="submit" name="auth" value="cancel">No, decline</button>
- </body>
-</html>
-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');
-    }
-}
-?>
index 5e743e0..8ce8997 100644 (file)
  * @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);
     }
 }
 ?>
index cb20832..1dfdec9 100644 (file)
  * @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 (file)
index 0000000..bad48b2
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..7cfd4e0
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..d1b26f8
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..b72224f
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+namespace OCA\Grauphel\Lib\Response;
+
+class ErrorResponse extends \OCP\AppFramework\Http\Response
+{
+    protected $error;
+
+    public function __construct($error)
+    {
+        $this->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 (file)
index 0000000..e7ce33d
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+namespace OCA\Grauphel\Lib\Response;
+
+class FormResponse extends \OCP\AppFramework\Http\Response
+{
+    protected $data;
+
+    public function __construct($data)
+    {
+        $this->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 (file)
index 0000000..b8cf873
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..ebb0783
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..c40ab33
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..e0de8f9
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Part of grauphel
+ *
+ * PHP version 5
+ *
+ * @category  Tools
+ * @package   Grauphel
+ * @author    Christian Weiske <cweiske@cweiske.de>
+ * @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 <cweiske@cweiske.de>
+ * @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 (file)
index 0000000..5d4023b
--- /dev/null
@@ -0,0 +1,9 @@
+<form method="post" action="<?php p($_['formaction']); ?>">
+ <input type="hidden" value="<?php p($_['requesttoken']); ?>" name="requesttoken" />
+ <input type="hidden" value="<?php p($_['oauth_token']); ?>" name="oauth_token" />
+ <p>
+  Shall application FIXME get full access to the notes?
+ </p>
+ <button type="submit" name="auth" value="ok">Yes, authorize</button>
+ <button type="submit" name="auth" value="cancel">No, decline</button>
+</form>