Try to fix issue #10; work around bug https://bugs.php.net/bug.php?id=68168
[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\RedirectResponse;
19 use \OCP\AppFramework\Http\TemplateResponse;
20
21 use \OCA\Grauphel\Lib\Client;
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 = OAuth::getProvider();
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             $newToken->client   = $token->client;
92             $this->deps->tokens->store($newToken);
93
94             return new FormResponse(
95                 array(
96                     'oauth_token'        => $newToken->tokenKey,
97                     'oauth_token_secret' => $newToken->secret,
98                 )
99             );
100         } catch (OAuthException $e) {
101             return new ErrorResponse($e->getMessage());
102         } catch (\OAuthException $e) {
103             $oauth->error($e);
104         }
105     }
106
107     /**
108      * Log the user in and let him authorize that the app may access notes
109      * OAuth step 2 of 3
110      *
111      * Page is not public and thus requires owncloud login
112      *
113      * @NoAdminRequired
114      * @NoCSRFRequired
115      */
116     public function authorize()
117     {
118         $token = $this->verifyRequestToken();
119         if (!$token instanceof Token) {
120             return $token;
121         }
122
123         $clientTitle = 'unknown';
124         $clientAgent = '';
125         if (isset($_GET['client'])) {
126             $clientAgent = $_GET['client'];
127             $cl = new Client();
128             $clientTitle = $cl->getNiceName($clientAgent);
129         }
130
131         $res = new TemplateResponse('grauphel', 'oauthAuthorize');
132         $res->setParams(
133             array(
134                 'oauth_token' => $token->tokenKey,
135                 'client'      => $clientTitle,
136                 'formaction'  => $this->deps->urlGen->linkToRoute(
137                     'grauphel.oauth.confirm'
138                 ) . '?client=' . urlencode($clientAgent),
139             )
140         );
141         return $res;
142     }
143
144     /**
145      * User confirms or declines the authorization request
146      * OAuth step 2.5 of 3
147      *
148      * @NoAdminRequired
149      */
150     public function confirm()
151     {
152         $token = $this->verifyRequestToken();
153         $oauth = new OAuth();
154         $oauth->setDeps($this->deps);
155
156         try {
157             $token = $this->deps->tokens->loadAndDelete('temp', $token->tokenKey);
158         } catch (OAuthException $e) {
159             return new ErrorResponse($e->getMessage());
160         }
161
162         $authState = isset($_POST['auth']) && $_POST['auth'] == 'ok';
163         if ($authState === false) {
164             //user declined
165
166             //http://wiki.oauth.net/w/page/12238543/ProblemReporting
167             $res = new RedirectResponse(
168                 UrlHelper::addParams(
169                     $token->callback,
170                     array(
171                         'oauth_token'   => $token->tokenKey,
172                         'oauth_problem' => 'permission_denied',
173                     )
174                 )
175             );
176             $res->setStatus(Http::STATUS_SEE_OTHER);
177             return $res;
178         }
179
180         $clientAgent = '';
181         if (isset($_GET['client'])) {
182             $clientAgent = $_GET['client'];
183         }
184
185         //the user is logged in and authorized
186         $provider = OAuth::getProvider();
187
188         $newToken = new Token('verify');
189         $newToken->tokenKey = $token->tokenKey;
190         $newToken->secret   = $token->secret;
191         $newToken->verifier = 'v' . bin2hex($provider->generateToken(8));
192         $newToken->user     = $this->user->getUID();
193         $newToken->client   = $clientAgent;
194
195         $this->deps->tokens->store($newToken);
196
197         //redirect
198         //FIXME: if no callback is given, show the token to the user
199         $res = new RedirectResponse(
200             UrlHelper::addParams(
201                 $token->callback,
202                 array(
203                     'oauth_token'    => $newToken->tokenKey,
204                     'oauth_verifier' => $newToken->verifier
205                 )
206             )
207         );
208         $res->setStatus(Http::STATUS_SEE_OTHER);
209         return $res;
210     }
211
212     protected function verifyRequestToken()
213     {
214         if (!isset($_REQUEST['oauth_token'])) {
215             return new ErrorResponse('oauth_token missing');
216         }
217
218         $oauth = new OAuth();
219         $oauth->setDeps($this->deps);
220         if (!$oauth->validateToken($_REQUEST['oauth_token'])) {
221             return new ErrorResponse('Invalid token string');
222         }
223
224         $reqToken = $_REQUEST['oauth_token'];
225
226         try {
227             $token = $this->deps->tokens->load('temp', $reqToken);
228         } catch (OAuthException $e) {
229             return new ErrorResponse($e->getMessage());
230         }
231
232         return $token;
233     }
234
235     /**
236      * Create and return a request token.
237      * OAuth step 1 of 3
238      *
239      * @NoAdminRequired
240      * @NoCSRFRequired
241      * @PublicPage
242      */
243     public function requestToken()
244     {
245         $oauth = new OAuth();
246         $oauth->setDeps($this->deps);
247         $urlGen = $this->deps->urlGen;
248
249         try {
250             $provider = OAuth::getProvider();
251             $oauth->registerHandler($provider);
252             $provider->isRequestTokenEndpoint(true);
253             $provider->checkOAuthRequest(
254                 $urlGen->getAbsoluteURL(
255                     $urlGen->linkToRoute('grauphel.oauth.requestToken')
256                 )
257             );
258
259             //store token + callback URI for later
260             $token = new Token('temp');
261             $token->tokenKey = 'r' . bin2hex($provider->generateToken(8));
262             $token->secret   = 's' . bin2hex($provider->generateToken(8));
263             $token->callback = $provider->callback;
264
265             $this->deps->tokens->store($token);
266
267             return new FormResponse(
268                 array(
269                     'oauth_token'              => $token->tokenKey,
270                     'oauth_token_secret'       => $token->secret,
271                     'oauth_callback_confirmed' => 'true'
272                 )
273             );
274         } catch (OAuthException $e) {
275             return new ErrorResponse($e->getMessage());
276         } catch (\OAuthException $e) {
277             $oauth->error($e);
278         }
279     }
280 }
281 ?>