diff options
author | Roeland Jago Douma <roeland@famdouma.nl> | 2018-05-18 12:28:52 +0200 |
---|---|---|
committer | Roeland Jago Douma <roeland@famdouma.nl> | 2018-06-20 08:53:35 +0200 |
commit | f36ef8ca80d92727857fe398491fce6eb17ee996 (patch) | |
tree | 5388c45bda5a719b6737d39756f2597cb658a0fa /lib/public/AppFramework | |
parent | cad8824a8e7da7fcf61960b6502b307672651c2b (diff) | |
download | nextcloud-server-f36ef8ca80d92727857fe398491fce6eb17ee996.tar.gz nextcloud-server-f36ef8ca80d92727857fe398491fce6eb17ee996.zip |
Add the new PublicShareController and PublicShareMiddleware
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
Diffstat (limited to 'lib/public/AppFramework')
-rw-r--r-- | lib/public/AppFramework/AuthPublicShareController.php | 188 | ||||
-rw-r--r-- | lib/public/AppFramework/PublicShareController.php | 138 |
2 files changed, 326 insertions, 0 deletions
diff --git a/lib/public/AppFramework/AuthPublicShareController.php b/lib/public/AppFramework/AuthPublicShareController.php new file mode 100644 index 00000000000..7740faa5855 --- /dev/null +++ b/lib/public/AppFramework/AuthPublicShareController.php @@ -0,0 +1,188 @@ +<?php +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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/>. + * + */ +declare(strict_types=1); + +namespace OCP\AppFramework; + +use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\Files\NotFoundException; +use OCP\IRequest; +use OCP\ISession; +use OCP\IURLGenerator; + +/** + * Base controller for interactive public shares + * + * It will verify if the user is properly authenticated to the share. If not the + * user will be redirected to an authentication page. + * + * Use this for a controller that is to be called directly by a user. So the + * normal public share page for files/calendars etc. + * + * @since 14.0.0 + */ +abstract class AuthPublicShareController extends PublicShareController { + + /** @var IURLGenerator */ + protected $urlGenerator; + + /** + * @since 14.0.0 + */ + public function __construct(string $appName, + IRequest $request, + ISession $session, + IURLGenerator $urlGenerator) { + parent::__construct($appName, $request, $session); + + $this->urlGenerator = $urlGenerator; + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * Show the authentication page + * The form has to submit to the authenticate method route + * + * @since 14.0.0 + */ + abstract public function showAuthenticate(): TemplateResponse; + + /** + * The template to show when authentication failed + * + * @since 14.0.0 + */ + abstract protected function showAuthFailed(): TemplateResponse; + + /** + * Verify the password + * + * @since 14.0.0 + */ + abstract protected function verifyPassword(string $password): bool; + + /** + * Function called after failed authentication + * + * You can use this to do some logging for example + * + * @since 14.0.0 + */ + protected function authFailed() { + } + + /** + * Function called after successfull authentication + * + * You can use this to do some logging for example + * + * @since 14.0.0 + */ + protected function authSucceeded() { + } + + /** + * @UseSession + * @PublicPage + * @BruteForceProtection(action=publicLinkAuth) + * + * Authenticate the share + * + * @since 14.0.0 + */ + final public function authenticate(string $password = '') { + // Already authenticated + if ($this->isAuthenticated()) { + return $this->getRedirect(); + } + + if (!$this->verifyPassword($password)) { + $this->authFailed(); + $response = $this->showAuthFailed(); + $response->throttle(); + return $response; + } + + $this->session->regenerateId(); + $response = $this->getRedirect(); + + $this->session->clear(); + $this->session->set('public_link_authenticated_token', $this->getToken()); + $this->session->set('public_link_authenticated_password_hash', $this->getPasswordHash()); + + $this->authSucceeded(); + + return $response; + } + + /** + * Default landing page + * + * @since 14.0.0 + */ + abstract public function showShare(): TemplateResponse; + + /** + * @since 14.0.0 + */ + final public function getAuthenticationRedirect(string $redirect): RedirectResponse { + return new RedirectResponse( + $this->urlGenerator->linkToRoute($this->getRoute('showAuthenticate'), ['token' => $this->getToken(), 'redirect' => $redirect]) + ); + } + + /** + * @since 14.0.0 + */ + private function getRoute(string $function): string { + $app = strtolower($this->appName); + $class = strtolower((new \ReflectionClass($this))->getShortName()); + + return $app . '.' . $class . '.' . $function; + } + + /** + * @since 14.0.0 + */ + private function getRedirect(): RedirectResponse { + //Get all the stored redirect parameters: + $params = $this->session->get('public_link_authenticate_redirect'); + + $route = $this->getRoute('showShare'); + + if ($params === null) { + $params = []; + } else { + $params = json_decode($params, true); + if (isset($params['_route'])) { + $route = $params['_route']; + unset($params['_route']); + } + } + + return new RedirectResponse($this->urlGenerator->linkToRoute($route, $params)); + } +} diff --git a/lib/public/AppFramework/PublicShareController.php b/lib/public/AppFramework/PublicShareController.php new file mode 100644 index 00000000000..64fbe9df40b --- /dev/null +++ b/lib/public/AppFramework/PublicShareController.php @@ -0,0 +1,138 @@ +<?php +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @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/>. + * + */ +declare(strict_types=1); + +namespace OCP\AppFramework; + +use OCP\IRequest; +use OCP\ISession; + +/** + * Base controller for public shares + * + * It will verify if the user is properly authenticated to the share. If not a 404 + * is thrown by the PublicShareMiddleware. + * + * Use this for example for a controller that is not to be called via a webbrowser + * directly. For example a PublicPreviewController. As this is not meant to be + * called by a user direclty. + * + * To show an auth page extend the AuthPublicShareController + * + * @since 14.0.0 + */ +abstract class PublicShareController extends Controller { + + /** @var ISession */ + protected $session; + + /** @var string */ + private $token; + + /** + * @since 14.0.0 + */ + public function __construct(string $appName, + IRequest $request, + ISession $session) { + parent::__construct($appName, $request); + + $this->session = $session; + } + + /** + * Middleware set the token for the request + * + * @since 14.0.0 + */ + final public function setToken(string $token) { + $this->token = $token; + } + + /** + * Get the token for this request + * + * @since 14.0.0 + */ + public function getToken(): string { + return $this->token; + } + + /** + * Get a hash of the password for this share + * + * To ensure access is blocked when the password to a share is changed we store + * a hash of the password for this token. + * + * @since 14.0.0 + */ + abstract protected function getPasswordHash(): string; + + /** + * Is the provided token a valid token + * + * This function is already called from the middleware directly after setting the token. + * + * @since 14.0.0 + */ + abstract public function isValidToken(): bool; + + /** + * Is a share with this token password protected + * + * @since 14.0.0 + */ + abstract protected function isPasswordProtected(): bool; + + /** + * Check if a share is authenticated or not + * + * @since 14.0.0 + */ + final public function isAuthenticated(): bool { + // Always authenticated against non password protected shares + if (!$this->isPasswordProtected()) { + return true; + } + + // If we are authenticated properly + if ($this->session->get('public_link_authenticated_token') === $this->getToken() && + $this->session->get('public_link_authenticated_password_hash') === $this->getPasswordHash()) { + return true; + } + + // Fail by default if nothing matches + return false; + } + + /** + * Function called if the share is not found. + * + * You can use this to do some logging for example + * + * @since 14.0.0 + */ + public function shareNotFound() { + + } +} |