aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Encryption/Exceptions/EncryptionFailedException.php
blob: 04ca6eba79caeab003fef676c08c64e76b8bd7b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

/**
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
 * SPDX-License-Identifier: AGPL-3.0-only
 */
namespace OC\Encryption\Exceptions;

use OCP\Encryption\Exceptions\GenericEncryptionException;

class EncryptionFailedException extends GenericEncryptionException {
}
14 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
<?php

declare(strict_types=1);
/**
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
 * SPDX-License-Identifier: AGPL-3.0-only
 */
namespace OC\AppFramework\Routing;

use OC\AppFramework\DependencyInjection\DIContainer;
use OC\Route\Router;

/**
 * Class RouteConfig
 * @package OC\AppFramework\routing
 */
class RouteConfig {
	/** @var DIContainer */
	private $container;

	/** @var Router */
	private $router;

	/** @var array */
	private $routes;

	/** @var string */
	private $appName;

	/** @var string[] */
	private $controllerNameCache = [];

	protected $rootUrlApps = [
		'cloud_federation_api',
		'core',
		'files_sharing',
		'files',
		'profile',
		'settings',
		'spreed',
	];

	/**
	 * @param \OC\AppFramework\DependencyInjection\DIContainer $container
	 * @param \OC\Route\Router $router
	 * @param array $routes
	 * @internal param $appName
	 */
	public function __construct(DIContainer $container, Router $router, $routes) {
		$this->routes = $routes;
		$this->container = $container;
		$this->router = $router;
		$this->appName = $container['AppName'];
	}

	/**
	 * The routes and resource will be registered to the \OCP\Route\IRouter
	 */
	public function register() {
		// parse simple
		$this->processIndexRoutes($this->routes);

		// parse resources
		$this->processIndexResources($this->routes);

		/*
		 * OCS routes go into a different collection
		 */
		$oldCollection = $this->router->getCurrentCollection();
		$this->router->useCollection($oldCollection . '.ocs');

		// parse ocs simple routes
		$this->processOCS($this->routes);

		// parse ocs simple routes
		$this->processOCSResources($this->routes);

		$this->router->useCollection($oldCollection);
	}

	private function processOCS(array $routes): void {
		$ocsRoutes = $routes['ocs'] ?? [];
		foreach ($ocsRoutes as $ocsRoute) {
			$this->processRoute($ocsRoute, 'ocs.');
		}
	}

	/**
	 * Creates one route base on the give configuration
	 * @param array $routes
	 * @throws \UnexpectedValueException
	 */
	private function processIndexRoutes(array $routes): void {
		$simpleRoutes = $routes['routes'] ?? [];
		foreach ($simpleRoutes as $simpleRoute) {
			$this->processRoute($simpleRoute);
		}
	}

	protected function processRoute(array $route, string $routeNamePrefix = ''): void {
		$name = $route['name'];
		$postfix = $route['postfix'] ?? '';
		$root = $this->buildRootPrefix($route, $routeNamePrefix);

		$url = $root . '/' . ltrim($route['url'], '/');
		$verb = strtoupper($route['verb'] ?? 'GET');

		$split = explode('#', $name, 2);
		if (count($split) !== 2) {
			throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
		}
		[$controller, $action] = $split;

		$controllerName = $this->buildControllerName($controller);
		$actionName = $this->buildActionName($action);

		/*
		 * The route name has to be lowercase, for symfony to match it correctly.
		 * This is required because smyfony allows mixed casing for controller names in the routes.
		 * To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
		 * This is also safe on the PHP side because class and method names collide regardless of the casing.
		 */
		$routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix);

		$router = $this->router->create($routeName, $url)
			->method($verb);

		// optionally register requirements for route. This is used to
		// tell the route parser how url parameters should be matched
		if (array_key_exists('requirements', $route)) {
			$router->requirements($route['requirements']);
		}

		// optionally register defaults for route. This is used to
		// tell the route parser how url parameters should be default valued
		$defaults = [];
		if (array_key_exists('defaults', $route)) {
			$defaults = $route['defaults'];
		}

		$defaults['caller'] = [$this->appName, $controllerName, $actionName];
		$router->defaults($defaults);
	}

	/**
	 * For a given name and url restful OCS routes are created:
	 *  - index
	 *  - show
	 *  - create
	 *  - update
	 *  - destroy
	 *
	 * @param array $routes
	 */
	private function processOCSResources(array $routes): void {
		$this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
	}

	/**
	 * For a given name and url restful routes are created:
	 *  - index
	 *  - show
	 *  - create
	 *  - update
	 *  - destroy
	 *
	 * @param array $routes
	 */
	private function processIndexResources(array $routes): void {
		$this->processResources($routes['resources'] ?? []);
	}

	/**
	 * For a given name and url restful routes are created:
	 *  - index
	 *  - show
	 *  - create
	 *  - update
	 *  - destroy
	 *
	 * @param array $resources
	 * @param string $routeNamePrefix
	 */
	protected function processResources(array $resources, string $routeNamePrefix = ''): void {
		// declaration of all restful actions
		$actions = [
			['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
			['name' => 'show', 'verb' => 'GET'],
			['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
			['name' => 'update', 'verb' => 'PUT'],
			['name' => 'destroy', 'verb' => 'DELETE'],
		];

		foreach ($resources as $resource => $config) {
			$root = $this->buildRootPrefix($config, $routeNamePrefix);

			// the url parameter used as id to the resource
			foreach ($actions as $action) {
				$url = $root . '/' . ltrim($config['url'], '/');
				$method = $action['name'];

				$verb = strtoupper($action['verb'] ?? 'GET');
				$collectionAction = $action['on-collection'] ?? false;
				if (!$collectionAction) {
					$url .= '/{id}';
				}
				if (isset($action['url-postfix'])) {
					$url .= '/' . $action['url-postfix'];
				}

				$controller = $resource;

				$controllerName = $this->buildControllerName($controller);
				$actionName = $this->buildActionName($method);

				$routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;

				$route = $this->router->create($routeName, $url)
					->method($verb);

				$route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
			}
		}
	}

	private function buildRootPrefix(array $route, string $routeNamePrefix): string {
		$defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
		$root = $route['root'] ?? $defaultRoot;

		if ($routeNamePrefix !== '') {
			// In OCS all apps are whitelisted
			return $root;
		}

		if (!\in_array($this->appName, $this->rootUrlApps, true)) {
			// Only allow root URLS for some apps
			return  $defaultRoot;
		}

		return $root;
	}

	/**
	 * Based on a given route name the controller name is generated
	 * @param string $controller
	 * @return string
	 */
	private function buildControllerName(string $controller): string {
		if (!isset($this->controllerNameCache[$controller])) {
			$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
		}
		return $this->controllerNameCache[$controller];
	}

	/**
	 * Based on the action part of the route name the controller method name is generated
	 * @param string $action
	 * @return string
	 */
	private function buildActionName(string $action): string {
		return $this->underScoreToCamelCase($action);
	}

	/**
	 * Underscored strings are converted to camel case strings
	 * @param string $str
	 * @return string
	 */
	private function underScoreToCamelCase(string $str): string {
		$pattern = '/_[a-z]?/';
		return preg_replace_callback(
			$pattern,
			function ($matches) {
				return strtoupper(ltrim($matches[0], '_'));
			},
			$str);
	}
}