oauth dance works
[grauphel.git] / controller / oauthcontroller.php
1 <?php
2 /**
3  * Part of grauphel
4  *
5  * PHP version 5
6  *
7  * @category  Tools
8  * @package   Grauphel
9  * @author    Christian Weiske <cweiske@cweiske.de>
10  * @copyright 2014 Christian Weiske
11  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
12  * @link      http://cweiske.de/grauphel.htm
13  */
14 namespace OCA\Grauphel\Controller;
15
16 use \OCP\AppFramework\Controller;
17 use \OCP\AppFramework\Http;
18 use \OCP\AppFramework\Http\JSONResponse;
19 use \OCP\AppFramework\Http\RedirectResponse;
20 use \OCP\AppFramework\Http\TemplateResponse;
21
22 use \OCA\Grauphel\Lib\Token;
23 use \OCA\Grauphel\Lib\OAuth;
24 use \OCA\Grauphel\Lib\Dependencies;
25 use \OCA\Grauphel\Lib\Response\ErrorResponse;
26 use \OCA\Grauphel\Lib\Response\FormResponse;
27 use \OCA\Grauphel\Lib\OAuthException;
28 use \OCA\Grauphel\Lib\UrlHelper;
29
30 /**
31  * OAuth handling
32  *
33  * @category  Tools
34  * @package   Grauphel
35  * @author    Christian Weiske <cweiske@cweiske.de>
36  * @copyright 2014 Christian Weiske
37  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
38  * @version   Release: @package_version@
39  * @link      http://cweiske.de/grauphel.htm
40  */
41 class OauthController extends Controller
42 {
43     protected $user;
44
45         /**
46          * constructor of the controller
47      *
48          * @param string   $appName Name of the app
49          * @param IRequest $request Instance of the request
50          */
51         public function __construct($appName, \OCP\IRequest $request, $user)
52     {
53         parent::__construct($appName, $request);
54         $this->user = $user;
55         $this->deps = Dependencies::get();
56
57         //default http header: we assume something is broken
58         header('HTTP/1.0 500 Internal Server Error');
59     }
60
61     /**
62      * Handle out an access token after verifying the verification token
63      * OAuth step 3 of 3
64      *
65      * @NoAdminRequired
66      * @NoCSRFRequired
67      * @PublicPage
68      */
69     public function accessToken()
70     {
71         $oauth = new OAuth();
72         $oauth->setDeps($this->deps);
73         $urlGen = $this->deps->urlGen;
74
75         try {
76             $provider = new \OAuthProvider();
77             $oauth->registerHandler($provider)
78                 ->registerVerificationTokenHandler($provider);
79             $provider->checkOAuthRequest(
80                 $urlGen->getAbsoluteURL(
81                     $urlGen->linkToRoute('grauphel.oauth.accessToken')
82                 )
83             );
84
85             $token = $this->deps->tokens->loadAndDelete('verify', $provider->token);
86
87             $newToken = new Token('access');
88             $newToken->tokenKey = 'a' . bin2hex($provider->generateToken(8));
89             $newToken->secret   = 's' . bin2hex($provider->generateToken(8));
90             $newToken->user     = $token->user;
91             $this->deps->tokens->store($newToken);
92
93             return new FormResponse(
94                 array(
95                     'oauth_token'        => $newToken->tokenKey,
96                     'oauth_token_secret' => $newToken->secret,
97                 )
98             );
99         } catch (OAuthException $e) {
100             return new ErrorResponse($e->getMessage());
101         } catch (\OAuthException $e) {
102             $oauth->error($e);
103         }
104     }
105
106     /**
107      * Log the user in and let him authorize that the app may access notes
108      * OAuth step 2 of 3
109      *
110      * Page is not public and thus requires owncloud login
111      *
112      * @NoAdminRequired
113      * @NoCSRFRequired
114      */
115     public function authorize()
116     {
117         $token = $this->verifyRequestToken();
118         if (!$token instanceof Token) {
119             return $token;
120         }
121
122         $res = new TemplateResponse('grauphel', 'oauthAuthorize');
123         $res->setParams(
124             array(
125                 'oauth_token' => $token->tokenKey,
126                 'formaction'  => $this->deps->urlGen->linkToRoute(
127                     'grauphel.oauth.confirm'
128                 ),
129             )
130         );
131         return $res;
132     }
133
134     /**
135      * User confirms or declines the authorization request
136      * OAuth step 2.5 of 3
137      *
138      * @NoAdminRequired
139      */
140     public function confirm()
141     {
142         $token = $this->verifyRequestToken();
143         $oauth = new OAuth();
144         $oauth->setDeps($this->deps);
145
146         try {
147             $token = $this->deps->tokens->loadAndDelete('temp', $token->tokenKey);
148         } catch (OAuthException $e) {
149             return new ErrorResponse($e->getMessage());
150         }
151
152         $authState = isset($_POST['auth']) && $_POST['auth'] == 'ok';
153         if ($authState === false) {
154             //user declined
155
156             //http://wiki.oauth.net/w/page/12238543/ProblemReporting
157             $res = new RedirectResponse(
158                 UrlHelper::addParams(
159                     $token->callback,
160                     array(
161                         'oauth_token'   => $token->tokenKey,
162                         'oauth_problem' => 'permission_denied',
163                     )
164                 )
165             );
166             $res->setStatus(Http::STATUS_SEE_OTHER);
167             return $res;
168         }
169
170         //the user is logged in and authorized
171         $provider = new \OAuthProvider();
172
173         $newToken = new Token('verify');
174         $newToken->tokenKey = $token->tokenKey;
175         $newToken->secret   = $token->secret;
176         $newToken->verifier = 'v' . bin2hex($provider->generateToken(8));
177         $newToken->user     = $this->user->getUID();
178
179         $this->deps->tokens->store($newToken);
180
181         //redirect
182         //FIXME: if no callback is given, show the token to the user
183         $res = new RedirectResponse(
184             UrlHelper::addParams(
185                 $token->callback,
186                 array(
187                     'oauth_token'    => $newToken->tokenKey,
188                     'oauth_verifier' => $newToken->verifier
189                 )
190             )
191         );
192         $res->setStatus(Http::STATUS_SEE_OTHER);
193         return $res;
194     }
195
196     protected function verifyRequestToken()
197     {
198         if (!isset($_REQUEST['oauth_token'])) {
199             return new ErrorResponse('oauth_token missing');
200         }
201
202         $oauth = new OAuth();
203         $oauth->setDeps($this->deps);
204         if (!$oauth->validateToken($_REQUEST['oauth_token'])) {
205             return new ErrorResponse('Invalid token string');
206         }
207
208         $reqToken = $_REQUEST['oauth_token'];
209
210         try {
211             $token = $this->deps->tokens->load('temp', $reqToken);
212         } catch (OAuthException $e) {
213             return new ErrorResponse($e->getMessage());
214         }
215
216         return $token;
217     }
218
219     /**
220      * Create and return a request token.
221      * OAuth step 1 of 3
222      *
223      * @NoAdminRequired
224      * @NoCSRFRequired
225      * @PublicPage
226      */
227     public function requestToken()
228     {
229         $oauth = new OAuth();
230         $oauth->setDeps($this->deps);
231         $urlGen = $this->deps->urlGen;
232
233         try {
234             $provider = new \OAuthProvider();
235             $oauth->registerHandler($provider);
236             $provider->isRequestTokenEndpoint(true);
237             $provider->checkOAuthRequest(
238                 $urlGen->getAbsoluteURL(
239                     $urlGen->linkToRoute('grauphel.oauth.requestToken')
240                 )
241             );
242
243             //store token + callback URI for later
244             $token = new Token('temp');
245             $token->tokenKey = 'r' . bin2hex($provider->generateToken(8));
246             $token->secret   = 's' . bin2hex($provider->generateToken(8));
247             $token->callback = $provider->callback;
248
249             $this->deps->tokens->store($token);
250
251             return new FormResponse(
252                 array(
253                     'oauth_token'              => $token->tokenKey,
254                     'oauth_token_secret'       => $token->secret,
255                     'oauth_callback_confirmed' => 'TRUE'
256                 )
257             );
258         } catch (OAuthException $e) {
259             return new ErrorResponse($e->getMessage());
260         } catch (\OAuthException $e) {
261             $oauth->error($e);
262         }
263     }
264 }
265 ?>