do not error out on basic auth
[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                 'clientTitle' => $clientTitle,
136                 'clientAgent' => $clientAgent,
137                 'formaction'  => $this->deps->urlGen->linkToRoute(
138                     'grauphel.oauth.confirm'
139                 ),
140             )
141         );
142         return $res;
143     }
144
145     /**
146      * User confirms or declines the authorization request
147      * OAuth step 2.5 of 3
148      *
149      * @NoAdminRequired
150      */
151     public function confirm()
152     {
153         $token = $this->verifyRequestToken();
154         $oauth = new OAuth();
155         $oauth->setDeps($this->deps);
156
157         try {
158             $token = $this->deps->tokens->loadAndDelete('temp', $token->tokenKey);
159         } catch (OAuthException $e) {
160             return new ErrorResponse($e->getMessage());
161         }
162
163         $authState = isset($_POST['auth']) && $_POST['auth'] == 'ok';
164         if ($authState === false) {
165             //user declined
166
167             //http://wiki.oauth.net/w/page/12238543/ProblemReporting
168             $res = new RedirectResponse(
169                 UrlHelper::addParams(
170                     $token->callback,
171                     array(
172                         'oauth_token'   => $token->tokenKey,
173                         'oauth_problem' => 'permission_denied',
174                     )
175                 )
176             );
177             $res->setStatus(Http::STATUS_SEE_OTHER);
178             return $res;
179         }
180
181         $clientAgent = '';
182         if (isset($_POST['client'])) {
183             $clientAgent = $_POST['client'];
184         }
185
186         //the user is logged in and authorized
187         $provider = OAuth::getProvider();
188
189         $newToken = new Token('verify');
190         $newToken->tokenKey = $token->tokenKey;
191         $newToken->secret   = $token->secret;
192         $newToken->verifier = 'v' . bin2hex($provider->generateToken(8));
193         $newToken->user     = $this->user->getUID();
194         $newToken->client   = $clientAgent;
195
196         $this->deps->tokens->store($newToken);
197
198         //redirect
199         //FIXME: if no callback is given, show the token to the user
200         $res = new RedirectResponse(
201             UrlHelper::addParams(
202                 $token->callback,
203                 array(
204                     'oauth_token'    => $newToken->tokenKey,
205                     'oauth_verifier' => $newToken->verifier
206                 )
207             )
208         );
209         $res->setStatus(Http::STATUS_SEE_OTHER);
210         return $res;
211     }
212
213     protected function verifyRequestToken()
214     {
215         if (!isset($_REQUEST['oauth_token'])) {
216             return new ErrorResponse('oauth_token missing');
217         }
218
219         $oauth = new OAuth();
220         $oauth->setDeps($this->deps);
221         if (!$oauth->validateToken($_REQUEST['oauth_token'])) {
222             return new ErrorResponse('Invalid token string');
223         }
224
225         $reqToken = $_REQUEST['oauth_token'];
226
227         try {
228             $token = $this->deps->tokens->load('temp', $reqToken);
229         } catch (OAuthException $e) {
230             return new ErrorResponse($e->getMessage());
231         }
232
233         return $token;
234     }
235
236     /**
237      * Create and return a request token.
238      * OAuth step 1 of 3
239      *
240      * @NoAdminRequired
241      * @NoCSRFRequired
242      * @PublicPage
243      */
244     public function requestToken()
245     {
246         $oauth = new OAuth();
247         $oauth->setDeps($this->deps);
248         $urlGen = $this->deps->urlGen;
249
250         try {
251             $provider = OAuth::getProvider();
252             $oauth->registerHandler($provider);
253             $provider->isRequestTokenEndpoint(true);
254             $provider->checkOAuthRequest(
255                 $urlGen->getAbsoluteURL(
256                     $urlGen->linkToRoute('grauphel.oauth.requestToken')
257                 )
258             );
259
260             //store token + callback URI for later
261             $token = new Token('temp');
262             $token->tokenKey = 'r' . bin2hex($provider->generateToken(8));
263             $token->secret   = 's' . bin2hex($provider->generateToken(8));
264             $token->callback = $provider->callback;
265
266             $this->deps->tokens->store($token);
267
268             return new FormResponse(
269                 array(
270                     'oauth_token'              => $token->tokenKey,
271                     'oauth_token_secret'       => $token->secret,
272                     'oauth_callback_confirmed' => 'true'
273                 )
274             );
275         } catch (OAuthException $e) {
276             return new ErrorResponse($e->getMessage());
277         } catch (\OAuthException $e) {
278             $oauth->error($e);
279         }
280     }
281 }
282 ?>