summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/lib/Connector/Sabre/Auth.php13
-rw-r--r--apps/oauth2/appinfo/database.xml79
-rw-r--r--apps/oauth2/appinfo/info.xml18
-rw-r--r--apps/oauth2/appinfo/routes.php46
-rw-r--r--apps/oauth2/lib/Controller/LoginRedirectorController.php78
-rw-r--r--apps/oauth2/lib/Controller/OauthApiController.php88
-rw-r--r--apps/oauth2/lib/Controller/SettingsController.php86
-rw-r--r--apps/oauth2/lib/Db/AccessToken.php53
-rw-r--r--apps/oauth2/lib/Db/AccessTokenMapper.php49
-rw-r--r--apps/oauth2/lib/Db/Client.php53
-rw-r--r--apps/oauth2/lib/Db/ClientMapper.php61
-rw-r--r--apps/oauth2/lib/Settings/Admin.php67
-rw-r--r--apps/oauth2/templates/admin.php70
-rw-r--r--core/Controller/ClientFlowLoginController.php92
-rw-r--r--core/templates/loginflow/authpicker.php2
-rw-r--r--core/templates/loginflow/redirect.php2
-rw-r--r--lib/private/User/Session.php4
17 files changed, 838 insertions, 23 deletions
diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php
index bdaf73d46e7..7ddbb70530a 100644
--- a/apps/dav/lib/Connector/Sabre/Auth.php
+++ b/apps/dav/lib/Connector/Sabre/Auth.php
@@ -210,6 +210,19 @@ class Auth extends AbstractBasic {
*/
private function auth(RequestInterface $request, ResponseInterface $response) {
$forcedLogout = false;
+
+ $authHeader = $request->getHeader('Authorization');
+ if (strpos($authHeader, 'Bearer ') !== false) {
+ if($this->userSession->tryTokenLogin($this->request)) {
+ $this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
+ $user = $this->userSession->getUser()->getUID();
+ \OC_Util::setupFS($user);
+ $this->currentUser = $user;
+ $this->session->close();
+ return [true, $this->principalPrefix . $user];
+ }
+ }
+
if(!$this->request->passesCSRFCheck() &&
$this->requiresCSRFCheck()) {
// In case of a fail with POST we need to recheck the credentials
diff --git a/apps/oauth2/appinfo/database.xml b/apps/oauth2/appinfo/database.xml
new file mode 100644
index 00000000000..2d7e3502db2
--- /dev/null
+++ b/apps/oauth2/appinfo/database.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+ <charset>utf8</charset>
+ <table>
+ <name>*dbprefix*oauth2_clients</name>
+ <declaration>
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <autoincrement>true</autoincrement>
+ <unsigned>true</unsigned>
+ <primary>true</primary>
+ </field>
+ <field>
+ <name>name</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>redirect_uri</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>2000</length>
+ </field>
+ <field>
+ <name>client_identifier</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ <field>
+ <name>secret</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+ </declaration>
+ </table>
+ <table>
+ <name>*dbprefix*oauth2_access_tokens</name>
+ <declaration>
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ <autoincrement>true</autoincrement>
+ <unsigned>true</unsigned>
+ <primary>true</primary>
+ </field>
+ <field>
+ <name>token_id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ </field>
+ <field>
+ <name>client_id</name>
+ <type>integer</type>
+ <notnull>true</notnull>
+ </field>
+ <field>
+ <name>hashed_code</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>128</length>
+ </field>
+ <field>
+ <name>encrypted_token</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>255</length>
+ </field>
+ </declaration>
+ </table>
+</database>
diff --git a/apps/oauth2/appinfo/info.xml b/apps/oauth2/appinfo/info.xml
new file mode 100644
index 00000000000..ebead97eb72
--- /dev/null
+++ b/apps/oauth2/appinfo/info.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<info>
+ <id>oauth2</id>
+ <name>OAuth 2.0</name>
+ <description>The OAuth2 app allows administrators to configure the built-in authentication workflow to also allow OAuth2 compatible authentication from other web applications.</description>
+ <licence>AGPL</licence>
+ <author>Lukas Reschke</author>
+ <namespace>OAuth2</namespace>
+ <version>1.0.3</version>
+ <default_enable/>
+ <types>
+ <authentication/>
+ </types>
+
+ <settings>
+ <admin>OCA\OAuth2\Settings\Admin</admin>
+ </settings>
+</info>
diff --git a/apps/oauth2/appinfo/routes.php b/apps/oauth2/appinfo/routes.php
new file mode 100644
index 00000000000..b088dff0d48
--- /dev/null
+++ b/apps/oauth2/appinfo/routes.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+return [
+ 'routes' => [
+ [
+ 'name' => 'Settings#addClient',
+ 'url' => '/settings',
+ 'verb' => 'POST',
+ ],
+ [
+ 'name' => 'Settings#deleteClient',
+ 'url' => '/clients/{id}/delete',
+ 'verb' => 'POST'
+ ],
+ [
+ 'name' => 'LoginRedirector#authorize',
+ 'url' => '/authorize',
+ 'verb' => 'GET',
+ ],
+ [
+ 'name' => 'OauthApi#getToken',
+ 'url' => '/api/v1/token',
+ // TODO: POST!
+ 'verb' => 'GET'
+ ],
+ ],
+]; \ No newline at end of file
diff --git a/apps/oauth2/lib/Controller/LoginRedirectorController.php b/apps/oauth2/lib/Controller/LoginRedirectorController.php
new file mode 100644
index 00000000000..1a2e00ef5dc
--- /dev/null
+++ b/apps/oauth2/lib/Controller/LoginRedirectorController.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Controller;
+
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+
+class LoginRedirectorController extends Controller {
+ /** @var IURLGenerator */
+ private $urlGenerator;
+ /** @var ClientMapper */
+ private $clientMapper;
+
+ /**
+ * @param string $appName
+ * @param IRequest $request
+ * @param IURLGenerator $urlGenerator
+ * @param ClientMapper $clientMapper
+ */
+ public function __construct($appName,
+ IRequest $request,
+ IURLGenerator $urlGenerator,
+ ClientMapper $clientMapper) {
+ parent::__construct($appName, $request);
+ $this->urlGenerator = $urlGenerator;
+ $this->clientMapper = $clientMapper;
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ *
+ * @param string $client_id
+ * @param string $redirect_uri
+ * @param string $state
+ * @return RedirectResponse
+ */
+ public function authorize($client_id,
+ $redirect_uri,
+ $state) {
+ $client = $this->clientMapper->getByIdentifier($client_id);
+
+ if($client->getRedirectUri() !== $redirect_uri) {
+ throw new \Exception('Redirect URI does not match');
+ }
+
+ $targetUrl = $this->urlGenerator->linkToRouteAbsolute(
+ 'core.ClientFlowLogin.showAuthPickerPage',
+ [
+ 'clientIdentifier' => $client->getClientIdentifier(),
+ 'oauthState' => $state,
+ ]
+ );
+ return new RedirectResponse($targetUrl);
+ }
+}
diff --git a/apps/oauth2/lib/Controller/OauthApiController.php b/apps/oauth2/lib/Controller/OauthApiController.php
new file mode 100644
index 00000000000..8432830bce3
--- /dev/null
+++ b/apps/oauth2/lib/Controller/OauthApiController.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Controller;
+
+use OC\Authentication\Token\DefaultTokenMapper;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\IRequest;
+use OCP\Security\ICrypto;
+use OCP\Security\ISecureRandom;
+
+class OauthApiController extends Controller {
+ /** @var AccessTokenMapper */
+ private $accessTokenMapper;
+ /** @var ICrypto */
+ private $crypto;
+ /** @var DefaultTokenMapper */
+ private $defaultTokenMapper;
+ /** @var ISecureRandom */
+ private $secureRandom;
+
+ /**
+ * @param string $appName
+ * @param IRequest $request
+ * @param ICrypto $crypto
+ * @param AccessTokenMapper $accessTokenMapper
+ * @param DefaultTokenMapper $defaultTokenMapper
+ * @param ISecureRandom $secureRandom
+ */
+ public function __construct($appName,
+ IRequest $request,
+ ICrypto $crypto,
+ AccessTokenMapper $accessTokenMapper,
+ DefaultTokenMapper $defaultTokenMapper,
+ ISecureRandom $secureRandom) {
+ parent::__construct($appName, $request);
+ $this->crypto = $crypto;
+ $this->accessTokenMapper = $accessTokenMapper;
+ $this->defaultTokenMapper = $defaultTokenMapper;
+ $this->secureRandom = $secureRandom;
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ *
+ * @param string $code
+ * @return JSONResponse
+ */
+ public function getToken($code) {
+ $accessToken = $this->accessTokenMapper->getByCode($code);
+ $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code);
+ $newCode = $this->secureRandom->generate(128);
+ $accessToken->setHashedCode(hash('sha512', $newCode));
+ $accessToken->setEncryptedToken($this->crypto->encrypt($decryptedToken, $newCode));
+ $this->accessTokenMapper->update($accessToken);
+
+ return new JSONResponse(
+ [
+ 'access_token' => $decryptedToken,
+ 'token_type' => 'token',
+ 'expires_in' => 3600,
+ 'refresh_token' => $newCode,
+ 'user_id' => ($this->defaultTokenMapper->getTokenById($accessToken->getTokenId()))->getUID(),
+ ]
+ );
+ }
+}
diff --git a/apps/oauth2/lib/Controller/SettingsController.php b/apps/oauth2/lib/Controller/SettingsController.php
new file mode 100644
index 00000000000..1d376694f5a
--- /dev/null
+++ b/apps/oauth2/lib/Controller/SettingsController.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Controller;
+
+use OCA\OAuth2\Db\Client;
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\Security\ISecureRandom;
+
+class SettingsController extends Controller {
+ /** @var IURLGenerator */
+ private $urlGenerator;
+ /** @var ClientMapper */
+ private $clientMapper;
+ /** @var ISecureRandom */
+ private $secureRandom;
+
+ const validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+ /**
+ * @param string $appName
+ * @param IRequest $request
+ * @param IURLGenerator $urlGenerator
+ * @param ClientMapper $clientMapper
+ * @param ISecureRandom $secureRandom
+ */
+ public function __construct($appName,
+ IRequest $request,
+ IURLGenerator $urlGenerator,
+ ClientMapper $clientMapper,
+ ISecureRandom $secureRandom) {
+ parent::__construct($appName, $request);
+ $this->urlGenerator = $urlGenerator;
+ $this->secureRandom = $secureRandom;
+ $this->clientMapper = $clientMapper;
+ }
+
+ /**
+ * @param string $name
+ * @param string $redirectUri
+ * @return RedirectResponse
+ */
+ public function addClient($name,
+ $redirectUri) {
+ $client = new Client();
+ $client->setName($name);
+ $client->setRedirectUri($redirectUri);
+ $client->setSecret($this->secureRandom->generate(64, self::validChars));
+ $client->setClientIdentifier($this->secureRandom->generate(64, self::validChars));
+ $this->clientMapper->insert($client);
+ return new RedirectResponse($this->urlGenerator->getAbsoluteURL('/index.php/settings/admin/security'));
+ }
+
+ /**
+ * @param int $id
+ * @return RedirectResponse
+ */
+ public function deleteClient($id) {
+ $client = new Client();
+ $client->setId($id);
+ $this->clientMapper->delete($client);
+ return new RedirectResponse($this->urlGenerator->getAbsoluteURL('/index.php/settings/admin/security'));
+ }
+}
diff --git a/apps/oauth2/lib/Db/AccessToken.php b/apps/oauth2/lib/Db/AccessToken.php
new file mode 100644
index 00000000000..8266a9a0068
--- /dev/null
+++ b/apps/oauth2/lib/Db/AccessToken.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Entity;
+
+/**
+ * @method int getTokenId()
+ * @method void setTokenId(int $identifier)
+ * @method int getClientId()
+ * @method void setClientId(int $identifier)
+ * @method string getEncryptedToken()
+ * @method void setEncryptedToken(string $token)
+ * @method string getHashedCode()
+ * @method void setHashedCode(string $token)
+ */
+class AccessToken extends Entity {
+ /** @var int */
+ protected $tokenId;
+ /** @var int */
+ protected $clientId;
+ /** @var string */
+ protected $hashedCode;
+ /** @var string */
+ protected $encryptedToken;
+
+ public function __construct() {
+ $this->addType('id', 'int');
+ $this->addType('token_id', 'int');
+ $this->addType('client_id', 'int');
+ $this->addType('hashed_code', 'string');
+ $this->addType('encrypted_token', 'string');
+ }
+}
diff --git a/apps/oauth2/lib/Db/AccessTokenMapper.php b/apps/oauth2/lib/Db/AccessTokenMapper.php
new file mode 100644
index 00000000000..0400ad6366c
--- /dev/null
+++ b/apps/oauth2/lib/Db/AccessTokenMapper.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Mapper;
+use OCP\IDBConnection;
+
+class AccessTokenMapper extends Mapper {
+
+ /**
+ * @param IDBConnection $db
+ */
+ public function __construct(IDBConnection $db) {
+ parent::__construct($db, 'oauth2_access_tokens');
+ }
+
+ /**
+ * @param string $code
+ * @return AccessToken
+ */
+ public function getByCode($code) {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where($qb->expr()->eq('hashed_code', $qb->createParameter('hashedCode')));
+
+ return $this->findEntity($qb->getSQL(), [hash('sha512', $code)]);
+ }
+}
diff --git a/apps/oauth2/lib/Db/Client.php b/apps/oauth2/lib/Db/Client.php
new file mode 100644
index 00000000000..85c1630cb15
--- /dev/null
+++ b/apps/oauth2/lib/Db/Client.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Entity;
+
+/**
+ * @method string getClientIdentifier()
+ * @method void setClientIdentifier(string $identifier)
+ * @method string getSecret()
+ * @method void setSecret(string $secret)
+ * @method string getRedirectUri()
+ * @method void setRedirectUri(string $redirectUri)
+ * @method string getName()
+ * @method void setName(string $name)
+ */
+class Client extends Entity {
+ /** @var string */
+ protected $name;
+ /** @var string */
+ protected $redirectUri;
+ /** @var string */
+ protected $clientIdentifier;
+ /** @var string */
+ protected $secret;
+
+ public function __construct() {
+ $this->addType('id', 'int');
+ $this->addType('name', 'string');
+ $this->addType('redirect_uri', 'string');
+ $this->addType('client_identifier', 'string');
+ $this->addType('secret', 'string');
+ }
+}
diff --git a/apps/oauth2/lib/Db/ClientMapper.php b/apps/oauth2/lib/Db/ClientMapper.php
new file mode 100644
index 00000000000..d3c09ac5c69
--- /dev/null
+++ b/apps/oauth2/lib/Db/ClientMapper.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Db;
+
+use OCP\AppFramework\Db\Mapper;
+use OCP\IDBConnection;
+
+class ClientMapper extends Mapper {
+
+ /**
+ * @param IDBConnection $db
+ */
+ public function __construct(IDBConnection $db) {
+ parent::__construct($db, 'oauth2_clients');
+ }
+
+ /**
+ * @param string $clientIdentifier
+ * @return Client
+ */
+ public function getByIdentifier($clientIdentifier) {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName)
+ ->where($qb->expr()->eq('client_identifier', $qb->createParameter('clientId')));
+
+ return $this->findEntity($qb->getSQL(), [$clientIdentifier]);
+ }
+
+ /**
+ * @return Client[]
+ */
+ public function getClients() {
+ $qb = $this->db->getQueryBuilder();
+ $qb
+ ->select('*')
+ ->from($this->tableName);
+
+ return $this->findEntities($qb->getSQL());
+ }
+}
diff --git a/apps/oauth2/lib/Settings/Admin.php b/apps/oauth2/lib/Settings/Admin.php
new file mode 100644
index 00000000000..aa120bcb7d7
--- /dev/null
+++ b/apps/oauth2/lib/Settings/Admin.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\OAuth2\Settings;
+
+use OCA\OAuth2\Db\ClientMapper;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IConfig;
+use OCP\Settings\ISettings;
+
+class Admin implements ISettings {
+ /** @var ClientMapper */
+ private $clientMapper;
+
+ /**
+ * @param ClientMapper $clientMapper
+ */
+ public function __construct(ClientMapper $clientMapper) {
+ $this->clientMapper = $clientMapper;
+ }
+
+ /**
+ * @return TemplateResponse
+ */
+ public function getForm() {
+ return new TemplateResponse(
+ 'oauth2',
+ 'admin',
+ [
+ 'clients' => $this->clientMapper->getClients(),
+ ],
+ ''
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSection() {
+ return 'security';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPriority() {
+ return 0;
+ }
+}
diff --git a/apps/oauth2/templates/admin.php b/apps/oauth2/templates/admin.php
new file mode 100644
index 00000000000..f5b8532e6b1
--- /dev/null
+++ b/apps/oauth2/templates/admin.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+$urlGenerator = \OC::$server->getURLGenerator();
+$themingDefaults = \OC::$server->getThemingDefaults();
+
+/** @var array $_ */
+/** @var \OCA\OAuth2\Db\Client[] $clients */
+$clients = $_['clients'];
+?>
+
+<div id="oauth2" class="section">
+ <h2><?php p($l->t('OAuth 2.0 clients')); ?></h2>
+ <p class="settings-hint"><?php p($l->t('OAuth 2.0 allows external services to request access to your %s.', [$themingDefaults->getName()])); ?></p>
+
+ <table class="grid">
+ <thead>
+ <tr>
+ <th id="headerName" scope="col"><?php p($l->t('Name')); ?></th>
+ <th id="headerRedirectUri" scope="col"><?php p($l->t('Redirection URI')); ?></th>
+ <th id="headerClientIdentifier" scope="col"><?php p($l->t('Client Identifier')); ?></th>
+ <th id="headerSecret" scope="col"><?php p($l->t('Secret')); ?></th>
+ <th id="headerRemove">&nbsp;</th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($clients as $client) { ?>
+ <tr>
+ <td><?php p($client->getName()); ?></td>
+ <td><?php p($client->getRedirectUri()); ?></td>
+ <td><code><?php p($client->getClientIdentifier()); ?></code></td>
+ <td><code><?php p($client->getSecret()); ?></code></td>
+ <td>
+ <form id="form-inline" class="delete" action="<?php p($urlGenerator->linkToRoute('oauth2.Settings.deleteClient', ['id' => $client->getId()])); ?>" method="POST">
+ <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
+ <input type="submit" class="button icon-delete" value="">
+ </form>
+ </td>
+ </tr>
+ <?php } ?>
+ </tbody>
+ </table>
+
+ <br/>
+ <h3><?php p($l->t('Add client')); ?></h3>
+ <form action="<?php p($urlGenerator->linkToRoute('oauth2.Settings.addClient')); ?>" method="POST">
+ <input type="text" id="name" name="name" placeholder="<?php p($l->t('Name')); ?>">
+ <input type="url" id="redirectUri" name="redirectUri" placeholder="<?php p($l->t('Redirection URI')); ?>">
+ <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
+ <input type="submit" class="button" value="<?php p($l->t('Add')); ?>">
+ </form>
+</div> \ No newline at end of file
diff --git a/core/Controller/ClientFlowLoginController.php b/core/Controller/ClientFlowLoginController.php
index 8c2c121d5b2..45287f3048c 100644
--- a/core/Controller/ClientFlowLoginController.php
+++ b/core/Controller/ClientFlowLoginController.php
@@ -25,6 +25,9 @@ use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken;
+use OCA\OAuth2\Db\AccessToken;
+use OCA\OAuth2\Db\AccessTokenMapper;
+use OCA\OAuth2\Db\ClientMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Response;
@@ -35,6 +38,7 @@ use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserSession;
+use OCP\Security\ICrypto;
use OCP\Security\ISecureRandom;
use OCP\Session\Exceptions\SessionNotAvailableException;
@@ -53,6 +57,12 @@ class ClientFlowLoginController extends Controller {
private $random;
/** @var IURLGenerator */
private $urlGenerator;
+ /** @var ClientMapper */
+ private $clientMapper;
+ /** @var AccessTokenMapper */
+ private $accessTokenMapper;
+ /** @var ICrypto */
+ private $crypto;
const stateName = 'client.flow.state.token';
@@ -66,6 +76,9 @@ class ClientFlowLoginController extends Controller {
* @param IProvider $tokenProvider
* @param ISecureRandom $random
* @param IURLGenerator $urlGenerator
+ * @param ClientMapper $clientMapper
+ * @param AccessTokenMapper $accessTokenMapper
+ * @param ICrypto $crypto
*/
public function __construct($appName,
IRequest $request,
@@ -75,7 +88,10 @@ class ClientFlowLoginController extends Controller {
ISession $session,
IProvider $tokenProvider,
ISecureRandom $random,
- IURLGenerator $urlGenerator) {
+ IURLGenerator $urlGenerator,
+ ClientMapper $clientMapper,
+ AccessTokenMapper $accessTokenMapper,
+ ICrypto $crypto) {
parent::__construct($appName, $request);
$this->userSession = $userSession;
$this->l10n = $l10n;
@@ -84,6 +100,9 @@ class ClientFlowLoginController extends Controller {
$this->tokenProvider = $tokenProvider;
$this->random = $random;
$this->urlGenerator = $urlGenerator;
+ $this->clientMapper = $clientMapper;
+ $this->accessTokenMapper = $accessTokenMapper;
+ $this->crypto = $crypto;
}
/**
@@ -126,31 +145,31 @@ class ClientFlowLoginController extends Controller {
* @NoCSRFRequired
* @UseSession
*
+ * @param string $clientIdentifier
+ *
* @return TemplateResponse
*/
- public function showAuthPickerPage() {
- if($this->userSession->isLoggedIn()) {
- return new TemplateResponse(
- $this->appName,
- '403',
- [
- 'file' => $this->l10n->t('Auth flow can only be started unauthenticated.'),
- ],
- 'guest'
- );
- }
-
+ public function showAuthPickerPage($clientIdentifier = '',
+ $oauthState = '') {
$stateToken = $this->random->generate(
64,
ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS
);
$this->session->set(self::stateName, $stateToken);
+ $clientName = $this->getClientName();
+ if($clientIdentifier !== '') {
+ $client = $this->clientMapper->getByIdentifier($clientIdentifier);
+ $clientName = $client->getName();
+ }
+
return new TemplateResponse(
$this->appName,
'loginflow/authpicker',
[
- 'client' => $this->getClientName(),
+ 'client' => $clientName,
+ 'clientIdentifier' => $clientIdentifier,
+ 'oauthState' => $oauthState,
'instanceName' => $this->defaults->getName(),
'urlGenerator' => $this->urlGenerator,
'stateToken' => $stateToken,
@@ -166,9 +185,13 @@ class ClientFlowLoginController extends Controller {
* @UseSession
*
* @param string $stateToken
+ * @param string $clientIdentifier
+ * @param string $oauthState
* @return TemplateResponse
*/
- public function redirectPage($stateToken = '') {
+ public function redirectPage($stateToken = '',
+ $clientIdentifier = '',
+ $oauthState = '') {
if(!$this->isValidToken($stateToken)) {
return $this->stateTokenForbiddenResponse();
}
@@ -179,6 +202,8 @@ class ClientFlowLoginController extends Controller {
[
'urlGenerator' => $this->urlGenerator,
'stateToken' => $stateToken,
+ 'clientIdentifier' => $clientIdentifier,
+ 'oauthState' => $oauthState,
],
'empty'
);
@@ -189,9 +214,15 @@ class ClientFlowLoginController extends Controller {
* @UseSession
*
* @param string $stateToken
+ * @param string $clientIdentifier
+ * @param string $state
+ * @param string $oauthState
* @return Http\RedirectResponse|Response
*/
- public function generateAppPassword($stateToken) {
+ public function generateAppPassword($stateToken,
+ $clientIdentifier = '',
+ $state = '',
+ $oauthState = '') {
if(!$this->isValidToken($stateToken)) {
$this->session->remove(self::stateName);
return $this->stateTokenForbiddenResponse();
@@ -222,9 +253,10 @@ class ClientFlowLoginController extends Controller {
}
$token = $this->random->generate(72);
- $this->tokenProvider->generateToken(
+ $uid = $this->userSession->getUser()->getUID();
+ $generatedToken = $this->tokenProvider->generateToken(
$token,
- $this->userSession->getUser()->getUID(),
+ $uid,
$loginName,
$password,
$this->getClientName(),
@@ -232,7 +264,27 @@ class ClientFlowLoginController extends Controller {
IToken::DO_NOT_REMEMBER
);
- return new Http\RedirectResponse('nc://login/server:' . $this->request->getServerHost() . '&user:' . urlencode($loginName) . '&password:' . urlencode($token));
- }
+ if($clientIdentifier !== '') {
+ $client = $this->clientMapper->getByIdentifier($clientIdentifier);
+
+ $code = $this->random->generate(128);
+ $accessToken = new AccessToken();
+ $accessToken->setClientId($client->getId());
+ $accessToken->setEncryptedToken($this->crypto->encrypt($token, $code));
+ $accessToken->setHashedCode(hash('sha512', $code));
+ $accessToken->setTokenId($generatedToken->getId());
+ $this->accessTokenMapper->insert($accessToken);
+
+ $redirectUri = sprintf(
+ '%s?state=%s&code=%s',
+ $client->getRedirectUri(),
+ urlencode($oauthState),
+ urlencode($code)
+ );
+ } else {
+ $redirectUri = 'nc://login/server:' . $this->request->getServerHost() . '&user:' . urlencode($loginName) . '&password:' . urlencode($token);
+ }
+ return new Http\RedirectResponse($redirectUri);
+ }
}
diff --git a/core/templates/loginflow/authpicker.php b/core/templates/loginflow/authpicker.php
index c5eb6cb316d..127a1156a68 100644
--- a/core/templates/loginflow/authpicker.php
+++ b/core/templates/loginflow/authpicker.php
@@ -35,7 +35,7 @@ $urlGenerator = $_['urlGenerator'];
<br/>
<p id="redirect-link">
- <a href="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.redirectPage', ['stateToken' => $_['stateToken']])) ?>">
+ <a href="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.redirectPage', ['stateToken' => $_['stateToken'], 'clientIdentifier' => $_['clientIdentifier'], 'oauthState' => $_['oauthState']])) ?>">
<input type="submit" class="login primary icon-confirm-white" value="<?php p('Grant access') ?>">
</a>
</p>
diff --git a/core/templates/loginflow/redirect.php b/core/templates/loginflow/redirect.php
index 7ef0184f61f..1c6e105b88d 100644
--- a/core/templates/loginflow/redirect.php
+++ b/core/templates/loginflow/redirect.php
@@ -31,7 +31,9 @@ $urlGenerator = $_['urlGenerator'];
</div>
<form method="POST" action="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.generateAppPassword')) ?>">
+ <input type="hidden" name="clientIdentifier" value="<?php p($_['clientIdentifier']) ?>" />
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
<input type="hidden" name="stateToken" value="<?php p($_['stateToken']) ?>" />
+ <input type="hidden" name="oauthState" value="<?php p($_['oauthState']) ?>" />
<input id="submit-redirect-form" type="submit" class="hidden "/>
</form>
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index f818666c374..0291c1baecb 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -725,7 +725,7 @@ class Session implements IUserSession, Emitter {
*/
public function tryTokenLogin(IRequest $request) {
$authHeader = $request->getHeader('Authorization');
- if (strpos($authHeader, 'token ') === false) {
+ if (strpos($authHeader, 'Bearer ') === false) {
// No auth header, let's try session id
try {
$token = $this->session->getId();
@@ -733,7 +733,7 @@ class Session implements IUserSession, Emitter {
return false;
}
} else {
- $token = substr($authHeader, 6);
+ $token = substr($authHeader, 7);
}
if (!$this->loginWithToken($token)) {