aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2013-08-17 11:16:48 +0200
committerThomas Müller <thomas.mueller@tmit.eu>2013-08-17 11:16:48 +0200
commitfde9cabe9774b67e88ee8aa8fa39fe044fe2da2f (patch)
tree6f08673a824cd19fd518e7df70115f9eb1a36900
parent33bb2238aeb0fddd3ddd3fe18307c5e7548e00bc (diff)
downloadnextcloud-server-fde9cabe9774b67e88ee8aa8fa39fe044fe2da2f.tar.gz
nextcloud-server-fde9cabe9774b67e88ee8aa8fa39fe044fe2da2f.zip
initial import of appframework
-rw-r--r--lib/appframework/app.php97
-rw-r--r--lib/appframework/controller/controller.php154
-rw-r--r--lib/appframework/core/api.php524
-rw-r--r--lib/appframework/dependencyinjection/dicontainer.php125
-rw-r--r--lib/appframework/http/dispatcher.php98
-rw-r--r--lib/appframework/http/downloadresponse.php51
-rw-r--r--lib/appframework/http/http.php208
-rw-r--r--lib/appframework/http/jsonresponse.php74
-rw-r--r--lib/appframework/http/redirectresponse.php54
-rw-r--r--lib/appframework/http/request.php217
-rw-r--r--lib/appframework/http/response.php169
-rw-r--r--lib/appframework/http/templateresponse.php126
-rw-r--r--lib/appframework/middleware/middleware.php100
-rw-r--r--lib/appframework/middleware/middlewaredispatcher.php159
-rw-r--r--lib/appframework/middleware/security/securityexception.php41
-rw-r--r--lib/appframework/middleware/security/securitymiddleware.php141
-rw-r--r--lib/appframework/routing/routeactionhandler.php42
-rw-r--r--lib/appframework/routing/routeconfig.php186
-rw-r--r--lib/appframework/utility/methodannotationreader.php61
-rw-r--r--lib/appframework/utility/timefactory.php42
-rw-r--r--tests/lib/appframework/AppTest.php107
-rw-r--r--tests/lib/appframework/classloader.php45
-rw-r--r--tests/lib/appframework/controller/ControllerTest.php161
-rw-r--r--tests/lib/appframework/dependencyinjection/DIContainerTest.php98
-rw-r--r--tests/lib/appframework/http/DispatcherTest.php218
-rw-r--r--tests/lib/appframework/http/DownloadResponseTest.php51
-rw-r--r--tests/lib/appframework/http/HttpTest.php87
-rw-r--r--tests/lib/appframework/http/JSONResponseTest.php96
-rw-r--r--tests/lib/appframework/http/RedirectResponseTest.php55
-rw-r--r--tests/lib/appframework/http/RequestTest.php78
-rw-r--r--tests/lib/appframework/http/ResponseTest.php119
-rw-r--r--tests/lib/appframework/http/TemplateResponseTest.php157
-rw-r--r--tests/lib/appframework/middleware/MiddlewareDispatcherTest.php280
-rw-r--r--tests/lib/appframework/middleware/MiddlewareTest.php82
-rw-r--r--tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php388
-rw-r--r--tests/lib/appframework/routing/RoutingTest.php214
-rw-r--r--tests/lib/appframework/utility/MethodAnnotationReaderTest.php58
37 files changed, 4963 insertions, 0 deletions
diff --git a/lib/appframework/app.php b/lib/appframework/app.php
new file mode 100644
index 00000000000..6224b858bbf
--- /dev/null
+++ b/lib/appframework/app.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework;
+
+use OC\AppFramework\DependencyInjection\DIContainer;
+
+
+/**
+ * Entry point for every request in your app. You can consider this as your
+ * public static void main() method
+ *
+ * Handles all the dependency injection, controllers and output flow
+ */
+class App {
+
+
+ /**
+ * Shortcut for calling a controller method and printing the result
+ * @param string $controllerName the name of the controller under which it is
+ * stored in the DI container
+ * @param string $methodName the method that you want to call
+ * @param array $urlParams an array with variables extracted from the routes
+ * @param DIContainer $container an instance of a pimple container.
+ */
+ public static function main($controllerName, $methodName, array $urlParams,
+ DIContainer $container) {
+ $container['urlParams'] = $urlParams;
+ $controller = $container[$controllerName];
+
+ // initialize the dispatcher and run all the middleware before the controller
+ $dispatcher = $container['Dispatcher'];
+
+ list($httpHeaders, $responseHeaders, $output) =
+ $dispatcher->dispatch($controller, $methodName);
+
+ if(!is_null($httpHeaders)) {
+ header($httpHeaders);
+ }
+
+ foreach($responseHeaders as $name => $value) {
+ header($name . ': ' . $value);
+ }
+
+ if(!is_null($output)) {
+ header('Content-Length: ' . strlen($output));
+ print($output);
+ }
+
+ }
+
+ /**
+ * Shortcut for calling a controller method and printing the result.
+ * Similar to App:main except that no headers will be sent.
+ * This should be used for example when registering sections via
+ * \OC\AppFramework\Core\API::registerAdmin()
+ *
+ * @param string $controllerName the name of the controller under which it is
+ * stored in the DI container
+ * @param string $methodName the method that you want to call
+ * @param array $urlParams an array with variables extracted from the routes
+ * @param DIContainer $container an instance of a pimple container.
+ */
+ public static function part($controllerName, $methodName, array $urlParams,
+ DIContainer $container){
+
+ $container['urlParams'] = $urlParams;
+ $controller = $container[$controllerName];
+
+ $dispatcher = $container['Dispatcher'];
+
+ list(, , $output) = $dispatcher->dispatch($controller, $methodName);
+ return $output;
+ }
+
+}
diff --git a/lib/appframework/controller/controller.php b/lib/appframework/controller/controller.php
new file mode 100644
index 00000000000..3e8166050d7
--- /dev/null
+++ b/lib/appframework/controller/controller.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Controller;
+
+use OC\AppFramework\Http\TemplateResponse;
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Core\API;
+
+
+/**
+ * Base class to inherit your controllers from
+ */
+abstract class Controller {
+
+ /**
+ * @var API instance of the api layer
+ */
+ protected $api;
+
+ protected $request;
+
+ /**
+ * @param API $api an api wrapper instance
+ * @param Request $request an instance of the request
+ */
+ public function __construct(API $api, Request $request){
+ $this->api = $api;
+ $this->request = $request;
+ }
+
+
+ /**
+ * Lets you access post and get parameters by the index
+ * @param string $key the key which you want to access in the URL Parameter
+ * placeholder, $_POST or $_GET array.
+ * The priority how they're returned is the following:
+ * 1. URL parameters
+ * 2. POST parameters
+ * 3. GET parameters
+ * @param mixed $default If the key is not found, this value will be returned
+ * @return mixed the content of the array
+ */
+ public function params($key, $default=null){
+ return isset($this->request->parameters[$key])
+ ? $this->request->parameters[$key]
+ : $default;
+ }
+
+
+ /**
+ * Returns all params that were received, be it from the request
+ * (as GET or POST) or throuh the URL by the route
+ * @return array the array with all parameters
+ */
+ public function getParams() {
+ return $this->request->parameters;
+ }
+
+
+ /**
+ * Returns the method of the request
+ * @return string the method of the request (POST, GET, etc)
+ */
+ public function method() {
+ return $this->request->method;
+ }
+
+
+ /**
+ * Shortcut for accessing an uploaded file through the $_FILES array
+ * @param string $key the key that will be taken from the $_FILES array
+ * @return array the file in the $_FILES element
+ */
+ public function getUploadedFile($key) {
+ return isset($this->request->files[$key]) ? $this->request->files[$key] : null;
+ }
+
+
+ /**
+ * Shortcut for getting env variables
+ * @param string $key the key that will be taken from the $_ENV array
+ * @return array the value in the $_ENV element
+ */
+ public function env($key) {
+ return isset($this->request->env[$key]) ? $this->request->env[$key] : null;
+ }
+
+
+ /**
+ * Shortcut for getting session variables
+ * @param string $key the key that will be taken from the $_SESSION array
+ * @return array the value in the $_SESSION element
+ */
+ public function session($key) {
+ return isset($this->request->session[$key]) ? $this->request->session[$key] : null;
+ }
+
+
+ /**
+ * Shortcut for getting cookie variables
+ * @param string $key the key that will be taken from the $_COOKIE array
+ * @return array the value in the $_COOKIE element
+ */
+ public function cookie($key) {
+ return isset($this->request->cookies[$key]) ? $this->request->cookies[$key] : null;
+ }
+
+
+ /**
+ * Shortcut for rendering a template
+ * @param string $templateName the name of the template
+ * @param array $params the template parameters in key => value structure
+ * @param string $renderAs user renders a full page, blank only your template
+ * admin an entry in the admin settings
+ * @param array $headers set additional headers in name/value pairs
+ * @return \OC\AppFramework\Http\TemplateResponse containing the page
+ */
+ public function render($templateName, array $params=array(),
+ $renderAs='user', array $headers=array()){
+ $response = new TemplateResponse($this->api, $templateName);
+ $response->setParams($params);
+ $response->renderAs($renderAs);
+
+ foreach($headers as $name => $value){
+ $response->addHeader($name, $value);
+ }
+
+ return $response;
+ }
+
+
+}
diff --git a/lib/appframework/core/api.php b/lib/appframework/core/api.php
new file mode 100644
index 00000000000..eb8ee01e5db
--- /dev/null
+++ b/lib/appframework/core/api.php
@@ -0,0 +1,524 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Core;
+
+
+/**
+ * This is used to wrap the owncloud static api calls into an object to make the
+ * code better abstractable for use in the dependency injection container
+ *
+ * Should you find yourself in need for more methods, simply inherit from this
+ * class and add your methods
+ */
+class API {
+
+ private $appName;
+
+ /**
+ * constructor
+ * @param string $appName the name of your application
+ */
+ public function __construct($appName){
+ $this->appName = $appName;
+ }
+
+
+ /**
+ * used to return the appname of the set application
+ * @return string the name of your application
+ */
+ public function getAppName(){
+ return $this->appName;
+ }
+
+
+ /**
+ * Creates a new navigation entry
+ * @param array $entry containing: id, name, order, icon and href key
+ */
+ public function addNavigationEntry(array $entry){
+ \OCP\App::addNavigationEntry($entry);
+ }
+
+
+ /**
+ * Gets the userid of the current user
+ * @return string the user id of the current user
+ */
+ public function getUserId(){
+ return \OCP\User::getUser();
+ }
+
+
+ /**
+ * Sets the current navigation entry to the currently running app
+ */
+ public function activateNavigationEntry(){
+ \OCP\App::setActiveNavigationEntry($this->appName);
+ }
+
+
+ /**
+ * Adds a new javascript file
+ * @param string $scriptName the name of the javascript in js/ without the suffix
+ * @param string $appName the name of the app, defaults to the current one
+ */
+ public function addScript($scriptName, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ \OCP\Util::addScript($appName, $scriptName);
+ }
+
+
+ /**
+ * Adds a new css file
+ * @param string $styleName the name of the css file in css/without the suffix
+ * @param string $appName the name of the app, defaults to the current one
+ */
+ public function addStyle($styleName, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ \OCP\Util::addStyle($appName, $styleName);
+ }
+
+
+ /**
+ * shorthand for addScript for files in the 3rdparty directory
+ * @param string $name the name of the file without the suffix
+ */
+ public function add3rdPartyScript($name){
+ \OCP\Util::addScript($this->appName . '/3rdparty', $name);
+ }
+
+
+ /**
+ * shorthand for addStyle for files in the 3rdparty directory
+ * @param string $name the name of the file without the suffix
+ */
+ public function add3rdPartyStyle($name){
+ \OCP\Util::addStyle($this->appName . '/3rdparty', $name);
+ }
+
+ /**
+ * Looks up a systemwide defined value
+ * @param string $key the key of the value, under which it was saved
+ * @return string the saved value
+ */
+ public function getSystemValue($key){
+ return \OCP\Config::getSystemValue($key, '');
+ }
+
+
+ /**
+ * Sets a new systemwide value
+ * @param string $key the key of the value, under which will be saved
+ * @param string $value the value that should be stored
+ */
+ public function setSystemValue($key, $value){
+ return \OCP\Config::setSystemValue($key, $value);
+ }
+
+
+ /**
+ * Looks up an appwide defined value
+ * @param string $key the key of the value, under which it was saved
+ * @return string the saved value
+ */
+ public function getAppValue($key, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ return \OCP\Config::getAppValue($appName, $key, '');
+ }
+
+
+ /**
+ * Writes a new appwide value
+ * @param string $key the key of the value, under which will be saved
+ * @param string $value the value that should be stored
+ */
+ public function setAppValue($key, $value, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ return \OCP\Config::setAppValue($appName, $key, $value);
+ }
+
+
+
+ /**
+ * Shortcut for setting a user defined value
+ * @param string $key the key under which the value is being stored
+ * @param string $value the value that you want to store
+ * @param string $userId the userId of the user that we want to store the value under, defaults to the current one
+ */
+ public function setUserValue($key, $value, $userId=null){
+ if($userId === null){
+ $userId = $this->getUserId();
+ }
+ \OCP\Config::setUserValue($userId, $this->appName, $key, $value);
+ }
+
+
+ /**
+ * Shortcut for getting a user defined value
+ * @param string $key the key under which the value is being stored
+ * @param string $userId the userId of the user that we want to store the value under, defaults to the current one
+ */
+ public function getUserValue($key, $userId=null){
+ if($userId === null){
+ $userId = $this->getUserId();
+ }
+ return \OCP\Config::getUserValue($userId, $this->appName, $key);
+ }
+
+
+ /**
+ * Returns the translation object
+ * @return \OC_L10N the translation object
+ */
+ public function getTrans(){
+ # TODO: use public api
+ return \OC_L10N::get($this->appName);
+ }
+
+
+ /**
+ * Used to abstract the owncloud database access away
+ * @param string $sql the sql query with ? placeholder for params
+ * @param int $limit the maximum number of rows
+ * @param int $offset from which row we want to start
+ * @return \OCP\DB a query object
+ */
+ public function prepareQuery($sql, $limit=null, $offset=null){
+ return \OCP\DB::prepare($sql, $limit, $offset);
+ }
+
+
+ /**
+ * Used to get the id of the just inserted element
+ * @param string $tableName the name of the table where we inserted the item
+ * @return int the id of the inserted element
+ */
+ public function getInsertId($tableName){
+ return \OCP\DB::insertid($tableName);
+ }
+
+
+ /**
+ * Returns the URL for a route
+ * @param string $routeName the name of the route
+ * @param array $arguments an array with arguments which will be filled into the url
+ * @return string the url
+ */
+ public function linkToRoute($routeName, $arguments=array()){
+ return \OCP\Util::linkToRoute($routeName, $arguments);
+ }
+
+
+ /**
+ * Returns an URL for an image or file
+ * @param string $file the name of the file
+ * @param string $appName the name of the app, defaults to the current one
+ */
+ public function linkTo($file, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ return \OCP\Util::linkTo($appName, $file);
+ }
+
+
+ /**
+ * Returns the link to an image, like link to but only with prepending img/
+ * @param string $file the name of the file
+ * @param string $appName the name of the app, defaults to the current one
+ */
+ public function imagePath($file, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ return \OCP\Util::imagePath($appName, $file);
+ }
+
+
+ /**
+ * Makes an URL absolute
+ * @param string $url the url
+ * @return string the absolute url
+ */
+ public function getAbsoluteURL($url){
+ # TODO: use public api
+ return \OC_Helper::makeURLAbsolute($url);
+ }
+
+
+ /**
+ * links to a file
+ * @param string $file the name of the file
+ * @param string $appName the name of the app, defaults to the current one
+ * @deprecated replaced with linkToRoute()
+ * @return string the url
+ */
+ public function linkToAbsolute($file, $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+ return \OCP\Util::linkToAbsolute($appName, $file);
+ }
+
+
+ /**
+ * Checks if the current user is logged in
+ * @return bool true if logged in
+ */
+ public function isLoggedIn(){
+ return \OCP\User::isLoggedIn();
+ }
+
+
+ /**
+ * Checks if a user is an admin
+ * @param string $userId the id of the user
+ * @return bool true if admin
+ */
+ public function isAdminUser($userId){
+ # TODO: use public api
+ return \OC_User::isAdminUser($userId);
+ }
+
+
+ /**
+ * Checks if a user is an subadmin
+ * @param string $userId the id of the user
+ * @return bool true if subadmin
+ */
+ public function isSubAdminUser($userId){
+ # TODO: use public api
+ return \OC_SubAdmin::isSubAdmin($userId);
+ }
+
+
+ /**
+ * Checks if the CSRF check was correct
+ * @return bool true if CSRF check passed
+ */
+ public function passesCSRFCheck(){
+ # TODO: use public api
+ return \OC_Util::isCallRegistered();
+ }
+
+
+ /**
+ * Checks if an app is enabled
+ * @param string $appName the name of an app
+ * @return bool true if app is enabled
+ */
+ public function isAppEnabled($appName){
+ return \OCP\App::isEnabled($appName);
+ }
+
+
+ /**
+ * Writes a function into the error log
+ * @param string $msg the error message to be logged
+ * @param int $level the error level
+ */
+ public function log($msg, $level=null){
+ switch($level){
+ case 'debug':
+ $level = \OCP\Util::DEBUG;
+ break;
+ case 'info':
+ $level = \OCP\Util::INFO;
+ break;
+ case 'warn':
+ $level = \OCP\Util::WARN;
+ break;
+ case 'fatal':
+ $level = \OCP\Util::FATAL;
+ break;
+ default:
+ $level = \OCP\Util::ERROR;
+ break;
+ }
+ \OCP\Util::writeLog($this->appName, $msg, $level);
+ }
+
+
+ /**
+ * Returns a template
+ * @param string $templateName the name of the template
+ * @param string $renderAs how it should be rendered
+ * @param string $appName the name of the app
+ * @return \OCP\Template a new template
+ */
+ public function getTemplate($templateName, $renderAs='user', $appName=null){
+ if($appName === null){
+ $appName = $this->appName;
+ }
+
+ if($renderAs === 'blank'){
+ return new \OCP\Template($appName, $templateName);
+ } else {
+ return new \OCP\Template($appName, $templateName, $renderAs);
+ }
+ }
+
+
+ /**
+ * turns an owncloud path into a path on the filesystem
+ * @param string path the path to the file on the oc filesystem
+ * @return string the filepath in the filesystem
+ */
+ public function getLocalFilePath($path){
+ # TODO: use public api
+ return \OC_Filesystem::getLocalFile($path);
+ }
+
+
+ /**
+ * used to return and open a new eventsource
+ * @return \OC_EventSource a new open EventSource class
+ */
+ public function openEventSource(){
+ # TODO: use public api
+ return new \OC_EventSource();
+ }
+
+ /**
+ * @brief connects a function to a hook
+ * @param string $signalClass class name of emitter
+ * @param string $signalName name of signal
+ * @param string $slotClass class name of slot
+ * @param string $slotName name of slot, in another word, this is the
+ * name of the method that will be called when registered
+ * signal is emitted.
+ * @return bool, always true
+ */
+ public function connectHook($signalClass, $signalName, $slotClass, $slotName) {
+ return \OCP\Util::connectHook($signalClass, $signalName, $slotClass, $slotName);
+ }
+
+ /**
+ * @brief Emits a signal. To get data from the slot use references!
+ * @param string $signalClass class name of emitter
+ * @param string $signalName name of signal
+ * @param array $params defautl: array() array with additional data
+ * @return bool, true if slots exists or false if not
+ */
+ public function emitHook($signalClass, $signalName, $params = array()) {
+ return \OCP\Util::emitHook($signalClass, $signalName, $params);
+ }
+
+ /**
+ * @brief clear hooks
+ * @param string $signalClass
+ * @param string $signalName
+ */
+ public function clearHook($signalClass=false, $signalName=false) {
+ if ($signalClass) {
+ \OC_Hook::clear($signalClass, $signalName);
+ }
+ }
+
+ /**
+ * Gets the content of an URL by using CURL or a fallback if it is not
+ * installed
+ * @param string $url the url that should be fetched
+ * @return string the content of the webpage
+ */
+ public function getUrlContent($url) {
+ return \OC_Util::getUrlContent($url);
+ }
+
+ /**
+ * Register a backgroundjob task
+ * @param string $className full namespace and class name of the class
+ * @param string $methodName the name of the static method that should be
+ * called
+ */
+ public function addRegularTask($className, $methodName) {
+ \OCP\Backgroundjob::addRegularTask($className, $methodName);
+ }
+
+ /**
+ * Tells ownCloud to include a template in the admin overview
+ * @param string $mainPath the path to the main php file without the php
+ * suffix, relative to your apps directory! not the template directory
+ * @param string $appName the name of the app, defaults to the current one
+ */
+ public function registerAdmin($mainPath, $appName=null) {
+ if($appName === null){
+ $appName = $this->appName;
+ }
+
+ \OCP\App::registerAdmin($appName, $mainPath);
+ }
+
+ /**
+ * Do a user login
+ * @param string $user the username
+ * @param string $password the password
+ * @return bool true if successful
+ */
+ public function login($user, $password) {
+ return \OC_User::login($user, $password);
+ }
+
+ /**
+ * @brief Loggs the user out including all the session data
+ * Logout, destroys session
+ */
+ public function logout() {
+ return \OCP\User::logout();
+ }
+
+ /**
+ * get the filesystem info
+ *
+ * @param string $path
+ * @return array with the following keys:
+ * - size
+ * - mtime
+ * - mimetype
+ * - encrypted
+ * - versioned
+ */
+ public function getFileInfo($path) {
+ return \OC\Files\Filesystem::getFileInfo($path);
+ }
+
+ /**
+ * get the view
+ *
+ * @return OC\Files\View instance
+ */
+ public function getView() {
+ return \OC\Files\Filesystem::getView();
+ }
+}
diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php
new file mode 100644
index 00000000000..34f64e72cb9
--- /dev/null
+++ b/lib/appframework/dependencyinjection/dicontainer.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\DependencyInjection;
+
+use OC\AppFramework\Http\Http;
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Http\Dispatcher;
+use OC\AppFramework\Core\API;
+use OC\AppFramework\Middleware\MiddlewareDispatcher;
+use OC\AppFramework\Middleware\Http\HttpMiddleware;
+use OC\AppFramework\Middleware\Security\SecurityMiddleware;
+use OC\AppFramework\Utility\TimeFactory;
+
+// register 3rdparty autoloaders
+require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php';
+
+
+/**
+ * This class extends Pimple (http://pimple.sensiolabs.org/) for reusability
+ * To use this class, extend your own container from this. Should you require it
+ * you can overwrite the dependencies with your own classes by simply redefining
+ * a dependency
+ */
+class DIContainer extends \Pimple {
+
+
+ /**
+ * Put your class dependencies in here
+ * @param string $appName the name of the app
+ */
+ public function __construct($appName){
+
+ $this['AppName'] = $appName;
+
+ $this['API'] = $this->share(function($c){
+ return new API($c['AppName']);
+ });
+
+ /**
+ * Http
+ */
+ $this['Request'] = $this->share(function($c) {
+ $params = json_decode(file_get_contents('php://input'), true);
+ $params = is_array($params) ? $params: array();
+
+ return new Request(
+ array(
+ 'get' => $_GET,
+ 'post' => $_POST,
+ 'files' => $_FILES,
+ 'server' => $_SERVER,
+ 'env' => $_ENV,
+ 'session' => $_SESSION,
+ 'cookies' => $_COOKIE,
+ 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD']))
+ ? $_SERVER['REQUEST_METHOD']
+ : null,
+ 'params' => $params,
+ 'urlParams' => $c['urlParams']
+ )
+ );
+ });
+
+ $this['Protocol'] = $this->share(function($c){
+ if(isset($_SERVER['SERVER_PROTOCOL'])) {
+ return new Http($_SERVER, $_SERVER['SERVER_PROTOCOL']);
+ } else {
+ return new Http($_SERVER);
+ }
+ });
+
+ $this['Dispatcher'] = $this->share(function($c) {
+ return new Dispatcher($c['Protocol'], $c['MiddlewareDispatcher']);
+ });
+
+
+ /**
+ * Middleware
+ */
+ $this['SecurityMiddleware'] = $this->share(function($c){
+ return new SecurityMiddleware($c['API'], $c['Request']);
+ });
+
+ $this['MiddlewareDispatcher'] = $this->share(function($c){
+ $dispatcher = new MiddlewareDispatcher();
+ $dispatcher->registerMiddleware($c['SecurityMiddleware']);
+
+ return $dispatcher;
+ });
+
+
+ /**
+ * Utilities
+ */
+ $this['TimeFactory'] = $this->share(function($c){
+ return new TimeFactory();
+ });
+
+
+ }
+
+
+}
diff --git a/lib/appframework/http/dispatcher.php b/lib/appframework/http/dispatcher.php
new file mode 100644
index 00000000000..ab5644274fa
--- /dev/null
+++ b/lib/appframework/http/dispatcher.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt, Thomas Tanghus, Bart Visscher
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+use \OC\AppFramework\Controller\Controller;
+use \OC\AppFramework\Middleware\MiddlewareDispatcher;
+
+
+/**
+ * Class to dispatch the request to the middleware disptacher
+ */
+class Dispatcher {
+
+ private $middlewareDispatcher;
+ private $protocol;
+
+
+ /**
+ * @param Http $protocol the http protocol with contains all status headers
+ * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which
+ * runs the middleware
+ */
+ public function __construct(Http $protocol,
+ MiddlewareDispatcher $middlewareDispatcher) {
+ $this->protocol = $protocol;
+ $this->middlewareDispatcher = $middlewareDispatcher;
+ }
+
+
+ /**
+ * Handles a request and calls the dispatcher on the controller
+ * @param Controller $controller the controller which will be called
+ * @param string $methodName the method name which will be called on
+ * the controller
+ * @return array $array[0] contains a string with the http main header,
+ * $array[1] contains headers in the form: $key => value, $array[2] contains
+ * the response output
+ */
+ public function dispatch(Controller $controller, $methodName) {
+ $out = array(null, array(), null);
+
+ try {
+
+ $this->middlewareDispatcher->beforeController($controller,
+ $methodName);
+ $response = $controller->$methodName();
+
+
+ // if an exception appears, the middleware checks if it can handle the
+ // exception and creates a response. If no response is created, it is
+ // assumed that theres no middleware who can handle it and the error is
+ // thrown again
+ } catch(\Exception $exception){
+ $response = $this->middlewareDispatcher->afterException(
+ $controller, $methodName, $exception);
+ }
+
+ $response = $this->middlewareDispatcher->afterController(
+ $controller, $methodName, $response);
+
+ // get the output which should be printed and run the after output
+ // middleware to modify the response
+ $output = $response->render();
+ $out[2] = $this->middlewareDispatcher->beforeOutput(
+ $controller, $methodName, $output);
+
+ // depending on the cache object the headers need to be changed
+ $out[0] = $this->protocol->getStatusHeader($response->getStatus(),
+ $response->getLastModified(), $response->getETag());
+ $out[1] = $response->getHeaders();
+
+ return $out;
+ }
+
+
+}
diff --git a/lib/appframework/http/downloadresponse.php b/lib/appframework/http/downloadresponse.php
new file mode 100644
index 00000000000..5a0db325fe6
--- /dev/null
+++ b/lib/appframework/http/downloadresponse.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+/**
+ * Prompts the user to download the a file
+ */
+abstract class DownloadResponse extends Response {
+
+ private $content;
+ private $filename;
+ private $contentType;
+
+ /**
+ * Creates a response that prompts the user to download the file
+ * @param string $filename the name that the downloaded file should have
+ * @param string $contentType the mimetype that the downloaded file should have
+ */
+ public function __construct($filename, $contentType) {
+ $this->filename = $filename;
+ $this->contentType = $contentType;
+
+ $this->addHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
+ $this->addHeader('Content-Type', $contentType);
+ }
+
+
+}
diff --git a/lib/appframework/http/http.php b/lib/appframework/http/http.php
new file mode 100644
index 00000000000..73f32d13b38
--- /dev/null
+++ b/lib/appframework/http/http.php
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt, Thomas Tanghus, Bart Visscher
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+class Http {
+
+ const STATUS_CONTINUE = 100;
+ const STATUS_SWITCHING_PROTOCOLS = 101;
+ const STATUS_PROCESSING = 102;
+ const STATUS_OK = 200;
+ const STATUS_CREATED = 201;
+ const STATUS_ACCEPTED = 202;
+ const STATUS_NON_AUTHORATIVE_INFORMATION = 203;
+ const STATUS_NO_CONTENT = 204;
+ const STATUS_RESET_CONTENT = 205;
+ const STATUS_PARTIAL_CONTENT = 206;
+ const STATUS_MULTI_STATUS = 207;
+ const STATUS_ALREADY_REPORTED = 208;
+ const STATUS_IM_USED = 226;
+ const STATUS_MULTIPLE_CHOICES = 300;
+ const STATUS_MOVED_PERMANENTLY = 301;
+ const STATUS_FOUND = 302;
+ const STATUS_SEE_OTHER = 303;
+ const STATUS_NOT_MODIFIED = 304;
+ const STATUS_USE_PROXY = 305;
+ const STATUS_RESERVED = 306;
+ const STATUS_TEMPORARY_REDIRECT = 307;
+ const STATUS_BAD_REQUEST = 400;
+ const STATUS_UNAUTHORIZED = 401;
+ const STATUS_PAYMENT_REQUIRED = 402;
+ const STATUS_FORBIDDEN = 403;
+ const STATUS_NOT_FOUND = 404;
+ const STATUS_METHOD_NOT_ALLOWED = 405;
+ const STATUS_NOT_ACCEPTABLE = 406;
+ const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407;
+ const STATUS_REQUEST_TIMEOUT = 408;
+ const STATUS_CONFLICT = 409;
+ const STATUS_GONE = 410;
+ const STATUS_LENGTH_REQUIRED = 411;
+ const STATUS_PRECONDITION_FAILED = 412;
+ const STATUS_REQUEST_ENTITY_TOO_LARGE = 413;
+ const STATUS_REQUEST_URI_TOO_LONG = 414;
+ const STATUS_UNSUPPORTED_MEDIA_TYPE = 415;
+ const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416;
+ const STATUS_EXPECTATION_FAILED = 417;
+ const STATUS_IM_A_TEAPOT = 418;
+ const STATUS_UNPROCESSABLE_ENTITY = 422;
+ const STATUS_LOCKED = 423;
+ const STATUS_FAILED_DEPENDENCY = 424;
+ const STATUS_UPGRADE_REQUIRED = 426;
+ const STATUS_PRECONDITION_REQUIRED = 428;
+ const STATUS_TOO_MANY_REQUESTS = 429;
+ const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
+ const STATUS_INTERNAL_SERVER_ERROR = 500;
+ const STATUS_NOT_IMPLEMENTED = 501;
+ const STATUS_BAD_GATEWAY = 502;
+ const STATUS_SERVICE_UNAVAILABLE = 503;
+ const STATUS_GATEWAY_TIMEOUT = 504;
+ const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505;
+ const STATUS_VARIANT_ALSO_NEGOTIATES = 506;
+ const STATUS_INSUFFICIENT_STORAGE = 507;
+ const STATUS_LOOP_DETECTED = 508;
+ const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509;
+ const STATUS_NOT_EXTENDED = 510;
+ const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511;
+
+ private $server;
+ private $protocolVersion;
+ protected $headers;
+
+ /**
+ * @param $_SERVER $server
+ * @param string $protocolVersion the http version to use defaults to HTTP/1.1
+ */
+ public function __construct($server, $protocolVersion='HTTP/1.1') {
+ $this->server = $server;
+ $this->protocolVersion = $protocolVersion;
+
+ $this->headers = array(
+ self::STATUS_CONTINUE => 'Continue',
+ self::STATUS_SWITCHING_PROTOCOLS => 'Switching Protocols',
+ self::STATUS_PROCESSING => 'Processing',
+ self::STATUS_OK => 'OK',
+ self::STATUS_CREATED => 'Created',
+ self::STATUS_ACCEPTED => 'Accepted',
+ self::STATUS_NON_AUTHORATIVE_INFORMATION => 'Non-Authorative Information',
+ self::STATUS_NO_CONTENT => 'No Content',
+ self::STATUS_RESET_CONTENT => 'Reset Content',
+ self::STATUS_PARTIAL_CONTENT => 'Partial Content',
+ self::STATUS_MULTI_STATUS => 'Multi-Status', // RFC 4918
+ self::STATUS_ALREADY_REPORTED => 'Already Reported', // RFC 5842
+ self::STATUS_IM_USED => 'IM Used', // RFC 3229
+ self::STATUS_MULTIPLE_CHOICES => 'Multiple Choices',
+ self::STATUS_MOVED_PERMANENTLY => 'Moved Permanently',
+ self::STATUS_FOUND => 'Found',
+ self::STATUS_SEE_OTHER => 'See Other',
+ self::STATUS_NOT_MODIFIED => 'Not Modified',
+ self::STATUS_USE_PROXY => 'Use Proxy',
+ self::STATUS_RESERVED => 'Reserved',
+ self::STATUS_TEMPORARY_REDIRECT => 'Temporary Redirect',
+ self::STATUS_BAD_REQUEST => 'Bad request',
+ self::STATUS_UNAUTHORIZED => 'Unauthorized',
+ self::STATUS_PAYMENT_REQUIRED => 'Payment Required',
+ self::STATUS_FORBIDDEN => 'Forbidden',
+ self::STATUS_NOT_FOUND => 'Not Found',
+ self::STATUS_METHOD_NOT_ALLOWED => 'Method Not Allowed',
+ self::STATUS_NOT_ACCEPTABLE => 'Not Acceptable',
+ self::STATUS_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',
+ self::STATUS_REQUEST_TIMEOUT => 'Request Timeout',
+ self::STATUS_CONFLICT => 'Conflict',
+ self::STATUS_GONE => 'Gone',
+ self::STATUS_LENGTH_REQUIRED => 'Length Required',
+ self::STATUS_PRECONDITION_FAILED => 'Precondition failed',
+ self::STATUS_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',
+ self::STATUS_REQUEST_URI_TOO_LONG => 'Request-URI Too Long',
+ self::STATUS_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',
+ self::STATUS_REQUEST_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable',
+ self::STATUS_EXPECTATION_FAILED => 'Expectation Failed',
+ self::STATUS_IM_A_TEAPOT => 'I\'m a teapot', // RFC 2324
+ self::STATUS_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', // RFC 4918
+ self::STATUS_LOCKED => 'Locked', // RFC 4918
+ self::STATUS_FAILED_DEPENDENCY => 'Failed Dependency', // RFC 4918
+ self::STATUS_UPGRADE_REQUIRED => 'Upgrade required',
+ self::STATUS_PRECONDITION_REQUIRED => 'Precondition required', // draft-nottingham-http-new-status
+ self::STATUS_TOO_MANY_REQUESTS => 'Too Many Requests', // draft-nottingham-http-new-status
+ self::STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', // draft-nottingham-http-new-status
+ self::STATUS_INTERNAL_SERVER_ERROR => 'Internal Server Error',
+ self::STATUS_NOT_IMPLEMENTED => 'Not Implemented',
+ self::STATUS_BAD_GATEWAY => 'Bad Gateway',
+ self::STATUS_SERVICE_UNAVAILABLE => 'Service Unavailable',
+ self::STATUS_GATEWAY_TIMEOUT => 'Gateway Timeout',
+ self::STATUS_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported',
+ self::STATUS_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates',
+ self::STATUS_INSUFFICIENT_STORAGE => 'Insufficient Storage', // RFC 4918
+ self::STATUS_LOOP_DETECTED => 'Loop Detected', // RFC 5842
+ self::STATUS_BANDWIDTH_LIMIT_EXCEEDED => 'Bandwidth Limit Exceeded', // non-standard
+ self::STATUS_NOT_EXTENDED => 'Not extended',
+ self::STATUS_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', // draft-nottingham-http-new-status
+ );
+ }
+
+
+ /**
+ * Gets the correct header
+ * @param Http::CONSTANT $status the constant from the Http class
+ * @param \DateTime $lastModified formatted last modified date
+ * @param string $Etag the etag
+ */
+ public function getStatusHeader($status, \DateTime $lastModified=null,
+ $ETag=null) {
+
+ if(!is_null($lastModified)) {
+ $lastModified = $lastModified->format(\DateTime::RFC2822);
+ }
+
+ // if etag or lastmodified have not changed, return a not modified
+ if ((isset($this->server['HTTP_IF_NONE_MATCH'])
+ && trim($this->server['HTTP_IF_NONE_MATCH']) === $ETag)
+
+ ||
+
+ (isset($this->server['HTTP_IF_MODIFIED_SINCE'])
+ && trim($this->server['HTTP_IF_MODIFIED_SINCE']) ===
+ $lastModified)) {
+
+ $status = self::STATUS_NOT_MODIFIED;
+ }
+
+ // we have one change currently for the http 1.0 header that differs
+ // from 1.1: STATUS_TEMPORARY_REDIRECT should be STATUS_FOUND
+ // if this differs any more, we want to create childclasses for this
+ if($status === self::STATUS_TEMPORARY_REDIRECT
+ && $this->protocolVersion === 'HTTP/1.0') {
+
+ $status = self::STATUS_FOUND;
+ }
+
+ return $this->protocolVersion . ' ' . $status . ' ' .
+ $this->headers[$status];
+ }
+
+
+}
+
+
diff --git a/lib/appframework/http/jsonresponse.php b/lib/appframework/http/jsonresponse.php
new file mode 100644
index 00000000000..750f8a2ad15
--- /dev/null
+++ b/lib/appframework/http/jsonresponse.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+/**
+ * A renderer for JSON calls
+ */
+class JSONResponse extends Response {
+
+ protected $data;
+
+
+ /**
+ * @param array|object $data the object or array that should be transformed
+ * @param int $statusCode the Http status code, defaults to 200
+ */
+ public function __construct($data=array(), $statusCode=Http::STATUS_OK) {
+ $this->data = $data;
+ $this->setStatus($statusCode);
+ $this->addHeader('X-Content-Type-Options', 'nosniff');
+ $this->addHeader('Content-type', 'application/json; charset=utf-8');
+ }
+
+
+ /**
+ * Returns the rendered json
+ * @return string the rendered json
+ */
+ public function render(){
+ return json_encode($this->data);
+ }
+
+ /**
+ * Sets values in the data json array
+ * @param array|object $params an array or object which will be transformed
+ * to JSON
+ */
+ public function setData($data){
+ $this->data = $data;
+ }
+
+
+ /**
+ * Used to get the set parameters
+ * @return array the data
+ */
+ public function getData(){
+ return $this->data;
+ }
+
+}
diff --git a/lib/appframework/http/redirectresponse.php b/lib/appframework/http/redirectresponse.php
new file mode 100644
index 00000000000..727e0fb642e
--- /dev/null
+++ b/lib/appframework/http/redirectresponse.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+/**
+ * Redirects to a different URL
+ */
+class RedirectResponse extends Response {
+
+ private $redirectURL;
+
+ /**
+ * Creates a response that redirects to a url
+ * @param string $redirectURL the url to redirect to
+ */
+ public function __construct($redirectURL) {
+ $this->redirectURL = $redirectURL;
+ $this->setStatus(Http::STATUS_TEMPORARY_REDIRECT);
+ $this->addHeader('Location', $redirectURL);
+ }
+
+
+ /**
+ * @return string the url to redirect
+ */
+ public function getRedirectURL() {
+ return $this->redirectURL;
+ }
+
+
+}
diff --git a/lib/appframework/http/request.php b/lib/appframework/http/request.php
new file mode 100644
index 00000000000..7d024c8605f
--- /dev/null
+++ b/lib/appframework/http/request.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * ownCloud - Request
+ *
+ * @author Thomas Tanghus
+ * @copyright 2013 Thomas Tanghus (thomas@tanghus.net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\AppFramework\Http;
+
+/**
+ * Class for accessing variables in the request.
+ * This class provides an immutable object with request variables.
+ */
+
+class Request implements \ArrayAccess, \Countable {
+
+ protected $items = array();
+ protected $allowedKeys = array(
+ 'get',
+ 'post',
+ 'files',
+ 'server',
+ 'env',
+ 'session',
+ 'cookies',
+ 'urlParams',
+ 'params',
+ 'parameters',
+ 'method'
+ );
+
+ /**
+ * @param array $vars An associative array with the following optional values:
+ * @param array 'params' the parsed json array
+ * @param array 'urlParams' the parameters which were matched from the URL
+ * @param array 'get' the $_GET array
+ * @param array 'post' the $_POST array
+ * @param array 'files' the $_FILES array
+ * @param array 'server' the $_SERVER array
+ * @param array 'env' the $_ENV array
+ * @param array 'session' the $_SESSION array
+ * @param array 'cookies' the $_COOKIE array
+ * @param string 'method' the request method (GET, POST etc)
+ * @see http://www.php.net/manual/en/reserved.variables.php
+ */
+ public function __construct(array $vars=array()) {
+
+ foreach($this->allowedKeys as $name) {
+ $this->items[$name] = isset($vars[$name])
+ ? $vars[$name]
+ : array();
+ }
+
+ $this->items['parameters'] = array_merge(
+ $this->items['params'],
+ $this->items['get'],
+ $this->items['post'],
+ $this->items['urlParams']
+ );
+
+ }
+
+ // Countable method.
+ public function count() {
+ return count(array_keys($this->items['parameters']));
+ }
+
+ /**
+ * ArrayAccess methods
+ *
+ * Gives access to the combined GET, POST and urlParams arrays
+ *
+ * Examples:
+ *
+ * $var = $request['myvar'];
+ *
+ * or
+ *
+ * if(!isset($request['myvar']) {
+ * // Do something
+ * }
+ *
+ * $request['myvar'] = 'something'; // This throws an exception.
+ *
+ * @param string $offset The key to lookup
+ * @return string|null
+ */
+ public function offsetExists($offset) {
+ return isset($this->items['parameters'][$offset]);
+ }
+
+ /**
+ * @see offsetExists
+ */
+ public function offsetGet($offset) {
+ return isset($this->items['parameters'][$offset])
+ ? $this->items['parameters'][$offset]
+ : null;
+ }
+
+ /**
+ * @see offsetExists
+ */
+ public function offsetSet($offset, $value) {
+ throw new \RuntimeException('You cannot change the contents of the request object');
+ }
+
+ /**
+ * @see offsetExists
+ */
+ public function offsetUnset($offset) {
+ throw new \RuntimeException('You cannot change the contents of the request object');
+ }
+
+ // Magic property accessors
+ public function __set($name, $value) {
+ throw new \RuntimeException('You cannot change the contents of the request object');
+ }
+
+ /**
+ * Access request variables by method and name.
+ * Examples:
+ *
+ * $request->post['myvar']; // Only look for POST variables
+ * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
+ * Looks in the combined GET, POST and urlParams array.
+ *
+ * if($request->method !== 'POST') {
+ * throw new Exception('This function can only be invoked using POST');
+ * }
+ *
+ * @param string $name The key to look for.
+ * @return mixed|null
+ */
+ public function __get($name) {
+ switch($name) {
+ case 'get':
+ case 'post':
+ case 'files':
+ case 'server':
+ case 'env':
+ case 'session':
+ case 'cookies':
+ case 'parameters':
+ case 'params':
+ case 'urlParams':
+ return isset($this->items[$name])
+ ? $this->items[$name]
+ : null;
+ break;
+ case 'method':
+ return $this->items['method'];
+ break;
+ default;
+ return isset($this[$name])
+ ? $this[$name]
+ : null;
+ break;
+ }
+ }
+
+
+ public function __isset($name) {
+ return isset($this->items['parameters'][$name]);
+ }
+
+
+ public function __unset($id) {
+ throw new \RunTimeException('You cannot change the contents of the request object');
+ }
+
+ /**
+ * Returns the value for a specific http header.
+ *
+ * This method returns null if the header did not exist.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function getHeader($name) {
+
+ $name = strtoupper(str_replace(array('-'),array('_'),$name));
+ if (isset($this->server['HTTP_' . $name])) {
+ return $this->server['HTTP_' . $name];
+ }
+
+ // There's a few headers that seem to end up in the top-level
+ // server array.
+ switch($name) {
+ case 'CONTENT_TYPE' :
+ case 'CONTENT_LENGTH' :
+ if (isset($this->server[$name])) {
+ return $this->server[$name];
+ }
+ break;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/lib/appframework/http/response.php b/lib/appframework/http/response.php
new file mode 100644
index 00000000000..50778105f24
--- /dev/null
+++ b/lib/appframework/http/response.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt, Thomas Tanghus, Bart Visscher
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+/**
+ * Base class for responses. Also used to just send headers
+ */
+class Response {
+
+ /**
+ * @var array default headers
+ */
+ private $headers = array(
+ 'Cache-Control' => 'no-cache, must-revalidate'
+ );
+
+
+ /**
+ * @var string
+ */
+ private $status = Http::STATUS_OK;
+
+
+ /**
+ * @var \DateTime
+ */
+ private $lastModified;
+
+
+ /**
+ * @var string
+ */
+ private $ETag;
+
+
+ /**
+ * Caches the response
+ * @param int $cacheSeconds the amount of seconds that should be cached
+ * if 0 then caching will be disabled
+ */
+ public function cacheFor($cacheSeconds) {
+
+ if($cacheSeconds > 0) {
+ $this->addHeader('Cache-Control', 'max-age=' . $cacheSeconds .
+ ', must-revalidate');
+ } else {
+ $this->addHeader('Cache-Control', 'no-cache, must-revalidate');
+ }
+
+ }
+
+
+ /**
+ * Adds a new header to the response that will be called before the render
+ * function
+ * @param string $name The name of the HTTP header
+ * @param string $value The value, null will delete it
+ */
+ public function addHeader($name, $value) {
+ if(is_null($value)) {
+ unset($this->headers[$name]);
+ } else {
+ $this->headers[$name] = $value;
+ }
+ }
+
+
+ /**
+ * Returns the set headers
+ * @return array the headers
+ */
+ public function getHeaders() {
+ $mergeWith = array();
+
+ if($this->lastModified) {
+ $mergeWith['Last-Modified'] =
+ $this->lastModified->format(\DateTime::RFC2822);
+ }
+
+ if($this->ETag) {
+ $mergeWith['ETag'] = '"' . $this->ETag . '"';
+ }
+
+ return array_merge($mergeWith, $this->headers);
+ }
+
+
+ /**
+ * By default renders no output
+ * @return null
+ */
+ public function render() {
+ return null;
+ }
+
+
+ /**
+ * Set response status
+ * @param int $status a HTTP status code, see also the STATUS constants
+ */
+ public function setStatus($status) {
+ $this->status = $status;
+ }
+
+
+ /**
+ * Get response status
+ */
+ public function getStatus() {
+ return $this->status;
+ }
+
+
+ /**
+ * @return string the etag
+ */
+ public function getETag() {
+ return $this->ETag;
+ }
+
+
+ /**
+ * @return string RFC2822 formatted last modified date
+ */
+ public function getLastModified() {
+ return $this->lastModified;
+ }
+
+
+ /**
+ * @param string $ETag
+ */
+ public function setETag($ETag) {
+ $this->ETag = $ETag;
+ }
+
+
+ /**
+ * @param \DateTime $lastModified
+ */
+ public function setLastModified($lastModified) {
+ $this->lastModified = $lastModified;
+ }
+
+
+}
diff --git a/lib/appframework/http/templateresponse.php b/lib/appframework/http/templateresponse.php
new file mode 100644
index 00000000000..0a32da4b1b4
--- /dev/null
+++ b/lib/appframework/http/templateresponse.php
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+use OC\AppFramework\Core\API;
+
+
+/**
+ * Response for a normal template
+ */
+class TemplateResponse extends Response {
+
+ protected $templateName;
+ protected $params;
+ protected $api;
+ protected $renderAs;
+ protected $appName;
+
+ /**
+ * @param API $api an API instance
+ * @param string $templateName the name of the template
+ * @param string $appName optional if you want to include a template from
+ * a different app
+ */
+ public function __construct(API $api, $templateName, $appName=null) {
+ $this->templateName = $templateName;
+ $this->appName = $appName;
+ $this->api = $api;
+ $this->params = array();
+ $this->renderAs = 'user';
+ }
+
+
+ /**
+ * Sets template parameters
+ * @param array $params an array with key => value structure which sets template
+ * variables
+ */
+ public function setParams(array $params){
+ $this->params = $params;
+ }
+
+
+ /**
+ * Used for accessing the set parameters
+ * @return array the params
+ */
+ public function getParams(){
+ return $this->params;
+ }
+
+
+ /**
+ * Used for accessing the name of the set template
+ * @return string the name of the used template
+ */
+ public function getTemplateName(){
+ return $this->templateName;
+ }
+
+
+ /**
+ * Sets the template page
+ * @param string $renderAs admin, user or blank. Admin also prints the admin
+ * settings header and footer, user renders the normal
+ * normal page including footer and header and blank
+ * just renders the plain template
+ */
+ public function renderAs($renderAs){
+ $this->renderAs = $renderAs;
+ }
+
+
+ /**
+ * Returns the set renderAs
+ * @return string the renderAs value
+ */
+ public function getRenderAs(){
+ return $this->renderAs;
+ }
+
+
+ /**
+ * Returns the rendered html
+ * @return string the rendered html
+ */
+ public function render(){
+
+ if($this->appName !== null){
+ $appName = $this->appName;
+ } else {
+ $appName = $this->api->getAppName();
+ }
+
+ $template = $this->api->getTemplate($this->templateName, $this->renderAs, $appName);
+
+ foreach($this->params as $key => $value){
+ $template->assign($key, $value);
+ }
+
+ return $template->fetchPage();
+ }
+
+}
diff --git a/lib/appframework/middleware/middleware.php b/lib/appframework/middleware/middleware.php
new file mode 100644
index 00000000000..4df88490468
--- /dev/null
+++ b/lib/appframework/middleware/middleware.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Middleware;
+
+use OC\AppFramework\Http\Response;
+
+
+/**
+ * Middleware is used to provide hooks before or after controller methods and
+ * deal with possible exceptions raised in the controller methods.
+ * They're modeled after Django's middleware system:
+ * https://docs.djangoproject.com/en/dev/topics/http/middleware/
+ */
+abstract class Middleware {
+
+
+ /**
+ * This is being run in normal order before the controller is being
+ * called which allows several modifications and checks
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ */
+ public function beforeController($controller, $methodName){
+
+ }
+
+
+ /**
+ * This is being run when either the beforeController method or the
+ * controller method itself is throwing an exception. The middleware is
+ * asked in reverse order to handle the exception and to return a response.
+ * If the response is null, it is assumed that the exception could not be
+ * handled and the error will be thrown again
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param \Exception $exception the thrown exception
+ * @throws \Exception the passed in exception if it cant handle it
+ * @return Response a Response object in case that the exception was handled
+ */
+ public function afterException($controller, $methodName, \Exception $exception){
+ throw $exception;
+ }
+
+
+ /**
+ * This is being run after a successful controllermethod call and allows
+ * the manipulation of a Response object. The middleware is run in reverse order
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param Response $response the generated response from the controller
+ * @return Response a Response object
+ */
+ public function afterController($controller, $methodName, Response $response){
+ return $response;
+ }
+
+
+ /**
+ * This is being run after the response object has been rendered and
+ * allows the manipulation of the output. The middleware is run in reverse order
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param string $output the generated output from a response
+ * @return string the output that should be printed
+ */
+ public function beforeOutput($controller, $methodName, $output){
+ return $output;
+ }
+
+}
diff --git a/lib/appframework/middleware/middlewaredispatcher.php b/lib/appframework/middleware/middlewaredispatcher.php
new file mode 100644
index 00000000000..c2d16134dc5
--- /dev/null
+++ b/lib/appframework/middleware/middlewaredispatcher.php
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Middleware;
+
+use OC\AppFramework\Controller\Controller;
+use OC\AppFramework\Http\Response;
+
+
+/**
+ * This class is used to store and run all the middleware in correct order
+ */
+class MiddlewareDispatcher {
+
+ /**
+ * @var array array containing all the middlewares
+ */
+ private $middlewares;
+
+ /**
+ * @var int counter which tells us what middlware was executed once an
+ * exception occurs
+ */
+ private $middlewareCounter;
+
+
+ /**
+ * Constructor
+ */
+ public function __construct(){
+ $this->middlewares = array();
+ $this->middlewareCounter = 0;
+ }
+
+
+ /**
+ * Adds a new middleware
+ * @param Middleware $middleware the middleware which will be added
+ */
+ public function registerMiddleware(Middleware $middleWare){
+ array_push($this->middlewares, $middleWare);
+ }
+
+
+ /**
+ * returns an array with all middleware elements
+ * @return array the middlewares
+ */
+ public function getMiddlewares(){
+ return $this->middlewares;
+ }
+
+
+ /**
+ * This is being run in normal order before the controller is being
+ * called which allows several modifications and checks
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ */
+ public function beforeController(Controller $controller, $methodName){
+ // we need to count so that we know which middlewares we have to ask in
+ // case theres an exception
+ for($i=0; $i<count($this->middlewares); $i++){
+ $this->middlewareCounter++;
+ $middleware = $this->middlewares[$i];
+ $middleware->beforeController($controller, $methodName);
+ }
+ }
+
+
+ /**
+ * This is being run when either the beforeController method or the
+ * controller method itself is throwing an exception. The middleware is asked
+ * in reverse order to handle the exception and to return a response.
+ * If the response is null, it is assumed that the exception could not be
+ * handled and the error will be thrown again
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param \Exception $exception the thrown exception
+ * @return Response a Response object if the middleware can handle the
+ * exception
+ * @throws \Exception the passed in exception if it cant handle it
+ */
+ public function afterException(Controller $controller, $methodName, \Exception $exception){
+ for($i=$this->middlewareCounter-1; $i>=0; $i--){
+ $middleware = $this->middlewares[$i];
+ try {
+ return $middleware->afterException($controller, $methodName, $exception);
+ } catch(\Exception $exception){
+ continue;
+ }
+ }
+ throw $exception;
+ }
+
+
+ /**
+ * This is being run after a successful controllermethod call and allows
+ * the manipulation of a Response object. The middleware is run in reverse order
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param Response $response the generated response from the controller
+ * @return Response a Response object
+ */
+ public function afterController(Controller $controller, $methodName, Response $response){
+ for($i=count($this->middlewares)-1; $i>=0; $i--){
+ $middleware = $this->middlewares[$i];
+ $response = $middleware->afterController($controller, $methodName, $response);
+ }
+ return $response;
+ }
+
+
+ /**
+ * This is being run after the response object has been rendered and
+ * allows the manipulation of the output. The middleware is run in reverse order
+ *
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param string $output the generated output from a response
+ * @return string the output that should be printed
+ */
+ public function beforeOutput(Controller $controller, $methodName, $output){
+ for($i=count($this->middlewares)-1; $i>=0; $i--){
+ $middleware = $this->middlewares[$i];
+ $output = $middleware->beforeOutput($controller, $methodName, $output);
+ }
+ return $output;
+ }
+
+}
diff --git a/lib/appframework/middleware/security/securityexception.php b/lib/appframework/middleware/security/securityexception.php
new file mode 100644
index 00000000000..b32a2769ff5
--- /dev/null
+++ b/lib/appframework/middleware/security/securityexception.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Middleware\Security;
+
+
+/**
+ * Thrown when the security middleware encounters a security problem
+ */
+class SecurityException extends \Exception {
+
+ /**
+ * @param string $msg the security error message
+ * @param bool $ajax true if it resulted because of an ajax request
+ */
+ public function __construct($msg, $code = 0) {
+ parent::__construct($msg, $code);
+ }
+
+}
diff --git a/lib/appframework/middleware/security/securitymiddleware.php b/lib/appframework/middleware/security/securitymiddleware.php
new file mode 100644
index 00000000000..7a715f309a0
--- /dev/null
+++ b/lib/appframework/middleware/security/securitymiddleware.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Middleware\Security;
+
+use OC\AppFramework\Controller\Controller;
+use OC\AppFramework\Http\Http;
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Http\Response;
+use OC\AppFramework\Http\JSONResponse;
+use OC\AppFramework\Http\RedirectResponse;
+use OC\AppFramework\Utility\MethodAnnotationReader;
+use OC\AppFramework\Middleware\Middleware;
+use OC\AppFramework\Core\API;
+
+
+/**
+ * Used to do all the authentication and checking stuff for a controller method
+ * It reads out the annotations of a controller method and checks which if
+ * security things should be checked and also handles errors in case a security
+ * check fails
+ */
+class SecurityMiddleware extends Middleware {
+
+ private $api;
+
+ /**
+ * @var \OC\AppFramework\Http\Request
+ */
+ private $request;
+
+ /**
+ * @param API $api an instance of the api
+ */
+ public function __construct(API $api, Request $request){
+ $this->api = $api;
+ $this->request = $request;
+ }
+
+
+ /**
+ * This runs all the security checks before a method call. The
+ * security checks are determined by inspecting the controller method
+ * annotations
+ * @param string/Controller $controller the controllername or string
+ * @param string $methodName the name of the method
+ * @throws SecurityException when a security check fails
+ */
+ public function beforeController($controller, $methodName){
+
+ // get annotations from comments
+ $annotationReader = new MethodAnnotationReader($controller, $methodName);
+
+ // this will set the current navigation entry of the app, use this only
+ // for normal HTML requests and not for AJAX requests
+ $this->api->activateNavigationEntry();
+
+ // security checks
+ if(!$annotationReader->hasAnnotation('IsLoggedInExemption')) {
+ if(!$this->api->isLoggedIn()) {
+ throw new SecurityException('Current user is not logged in', Http::STATUS_UNAUTHORIZED);
+ }
+ }
+
+ if(!$annotationReader->hasAnnotation('IsAdminExemption')) {
+ if(!$this->api->isAdminUser($this->api->getUserId())) {
+ throw new SecurityException('Logged in user must be an admin', Http::STATUS_FORBIDDEN);
+ }
+ }
+
+ if(!$annotationReader->hasAnnotation('IsSubAdminExemption')) {
+ if(!$this->api->isSubAdminUser($this->api->getUserId())) {
+ throw new SecurityException('Logged in user must be a subadmin', Http::STATUS_FORBIDDEN);
+ }
+ }
+
+ if(!$annotationReader->hasAnnotation('CSRFExemption')) {
+ if(!$this->api->passesCSRFCheck()) {
+ throw new SecurityException('CSRF check failed', Http::STATUS_PRECONDITION_FAILED);
+ }
+ }
+
+ }
+
+
+ /**
+ * If an SecurityException is being caught, ajax requests return a JSON error
+ * response and non ajax requests redirect to the index
+ * @param Controller $controller the controller that is being called
+ * @param string $methodName the name of the method that will be called on
+ * the controller
+ * @param \Exception $exception the thrown exception
+ * @throws \Exception the passed in exception if it cant handle it
+ * @return Response a Response object or null in case that the exception could not be handled
+ */
+ public function afterException($controller, $methodName, \Exception $exception){
+ if($exception instanceof SecurityException){
+
+ if (stripos($this->request->getHeader('Accept'),'html')===false) {
+
+ $response = new JSONResponse(
+ array('message' => $exception->getMessage()),
+ $exception->getCode()
+ );
+ $this->api->log($exception->getMessage(), 'debug');
+ } else {
+
+ $url = $this->api->linkToAbsolute('index.php', ''); // TODO: replace with link to route
+ $response = new RedirectResponse($url);
+ $this->api->log($exception->getMessage(), 'debug');
+ }
+
+ return $response;
+
+ }
+
+ throw $exception;
+ }
+
+}
diff --git a/lib/appframework/routing/routeactionhandler.php b/lib/appframework/routing/routeactionhandler.php
new file mode 100644
index 00000000000..7fb56f14eab
--- /dev/null
+++ b/lib/appframework/routing/routeactionhandler.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * ownCloud - App Framework
+ *
+ * @author Thomas Müller
+ * @copyright 2013 Thomas Müller thomas.mueller@tmit.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\AppFramework\routing;
+
+use \OC\AppFramework\App;
+use \OC\AppFramework\DependencyInjection\DIContainer;
+
+class RouteActionHandler {
+ private $controllerName;
+ private $actionName;
+ private $container;
+
+ public function __construct(DIContainer $container, $controllerName, $actionName) {
+ $this->controllerName = $controllerName;
+ $this->actionName = $actionName;
+ $this->container = $container;
+ }
+
+ public function __invoke($params) {
+ App::main($this->controllerName, $this->actionName, $params, $this->container);
+ }
+}
diff --git a/lib/appframework/routing/routeconfig.php b/lib/appframework/routing/routeconfig.php
new file mode 100644
index 00000000000..53ab11bf2f5
--- /dev/null
+++ b/lib/appframework/routing/routeconfig.php
@@ -0,0 +1,186 @@
+<?php
+/**
+ * ownCloud - App Framework
+ *
+ * @author Thomas Müller
+ * @copyright 2013 Thomas Müller thomas.mueller@tmit.eu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\AppFramework\routing;
+
+use OC\AppFramework\DependencyInjection\DIContainer;
+
+/**
+ * Class RouteConfig
+ * @package OC\AppFramework\routing
+ */
+class RouteConfig {
+ private $container;
+ private $router;
+ private $routes;
+ private $appName;
+
+ /**
+ * @param \OC\AppFramework\DependencyInjection\DIContainer $container
+ * @param \OC_Router $router
+ * @param string $pathToYml
+ * @internal param $appName
+ */
+ public function __construct(DIContainer $container, \OC_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 \OC_Router
+ */
+ public function register() {
+
+ // parse simple
+ $this->processSimpleRoutes($this->routes);
+
+ // parse resources
+ $this->processResources($this->routes);
+ }
+
+ /**
+ * Creates one route base on the give configuration
+ * @param $routes
+ * @throws \UnexpectedValueException
+ */
+ private function processSimpleRoutes($routes)
+ {
+ $simpleRoutes = isset($routes['routes']) ? $routes['routes'] : array();
+ foreach ($simpleRoutes as $simpleRoute) {
+ $name = $simpleRoute['name'];
+ $url = $simpleRoute['url'];
+ $verb = isset($simpleRoute['verb']) ? strtoupper($simpleRoute['verb']) : 'GET';
+
+ $split = explode('#', $name, 2);
+ if (count($split) != 2) {
+ throw new \UnexpectedValueException('Invalid route name');
+ }
+ $controller = $split[0];
+ $action = $split[1];
+
+ $controllerName = $this->buildControllerName($controller);
+ $actionName = $this->buildActionName($action);
+
+ // register the route
+ $handler = new RouteActionHandler($this->container, $controllerName, $actionName);
+ $this->router->create($this->appName.'.'.$controller.'.'.$action, $url)->method($verb)->action($handler);
+ }
+ }
+
+ /**
+ * For a given name and url restful routes are created:
+ * - index
+ * - show
+ * - new
+ * - create
+ * - update
+ * - destroy
+ *
+ * @param $routes
+ */
+ private function processResources($routes)
+ {
+ // declaration of all restful actions
+ $actions = array(
+ array('name' => 'index', 'verb' => 'GET', 'on-collection' => true),
+ array('name' => 'show', 'verb' => 'GET'),
+ array('name' => 'create', 'verb' => 'POST', 'on-collection' => true),
+ array('name' => 'update', 'verb' => 'PUT'),
+ array('name' => 'destroy', 'verb' => 'DELETE'),
+ );
+
+ $resources = isset($routes['resources']) ? $routes['resources'] : array();
+ foreach ($resources as $resource => $config) {
+
+ // the url parameter used as id to the resource
+ $resourceId = $this->buildResourceId($resource);
+ foreach($actions as $action) {
+ $url = $config['url'];
+ $method = $action['name'];
+ $verb = isset($action['verb']) ? strtoupper($action['verb']) : 'GET';
+ $collectionAction = isset($action['on-collection']) ? $action['on-collection'] : false;
+ if (!$collectionAction) {
+ $url = $url . '/' . $resourceId;
+ }
+ if (isset($action['url-postfix'])) {
+ $url = $url . '/' . $action['url-postfix'];
+ }
+
+ $controller = $resource;
+
+ $controllerName = $this->buildControllerName($controller);
+ $actionName = $this->buildActionName($method);
+
+ $routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
+
+ $this->router->create($routeName, $url)->method($verb)->action(
+ new RouteActionHandler($this->container, $controllerName, $actionName)
+ );
+ }
+ }
+ }
+
+ /**
+ * Based on a given route name the controller name is generated
+ * @param $controller
+ * @return string
+ */
+ private function buildControllerName($controller)
+ {
+ return $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
+ }
+
+ /**
+ * Based on the action part of the route name the controller method name is generated
+ * @param $action
+ * @return string
+ */
+ private function buildActionName($action) {
+ return $this->underScoreToCamelCase($action);
+ }
+
+ /**
+ * Generates the id used in the url part o the route url
+ * @param $resource
+ * @return string
+ */
+ private function buildResourceId($resource) {
+ return '{'.$this->underScoreToCamelCase(rtrim($resource, 's')).'Id}';
+ }
+
+ /**
+ * Underscored strings are converted to camel case strings
+ * @param $str string
+ * @return string
+ */
+ private function underScoreToCamelCase($str) {
+ $pattern = "/_[a-z]?/";
+ return preg_replace_callback(
+ $pattern,
+ function ($matches) {
+ return strtoupper(ltrim($matches[0], "_"));
+ },
+ $str);
+ }
+}
diff --git a/lib/appframework/utility/methodannotationreader.php b/lib/appframework/utility/methodannotationreader.php
new file mode 100644
index 00000000000..42060a08529
--- /dev/null
+++ b/lib/appframework/utility/methodannotationreader.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Utility;
+
+
+/**
+ * Reads and parses annotations from doc comments
+ */
+class MethodAnnotationReader {
+
+ private $annotations;
+
+ /**
+ * @param object $object an object or classname
+ * @param string $method the method which we want to inspect for annotations
+ */
+ public function __construct($object, $method){
+ $this->annotations = array();
+
+ $reflection = new \ReflectionMethod($object, $method);
+ $docs = $reflection->getDocComment();
+
+ // extract everything prefixed by @ and first letter uppercase
+ preg_match_all('/@([A-Z]\w+)/', $docs, $matches);
+ $this->annotations = $matches[1];
+ }
+
+
+ /**
+ * Check if a method contains an annotation
+ * @param string $name the name of the annotation
+ * @return bool true if the annotation is found
+ */
+ public function hasAnnotation($name){
+ return in_array($name, $this->annotations);
+ }
+
+
+}
diff --git a/lib/appframework/utility/timefactory.php b/lib/appframework/utility/timefactory.php
new file mode 100644
index 00000000000..2c3dd6cf5e3
--- /dev/null
+++ b/lib/appframework/utility/timefactory.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Utility;
+
+
+/**
+ * Needed to mock calls to time()
+ */
+class TimeFactory {
+
+
+ /**
+ * @return int the result of a call to time()
+ */
+ public function getTime() {
+ return time();
+ }
+
+
+}
diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php
new file mode 100644
index 00000000000..000094d07c8
--- /dev/null
+++ b/tests/lib/appframework/AppTest.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework;
+
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Core\API;
+use OC\AppFramework\Middleware\MiddlewareDispatcher;
+
+// FIXME: loading pimpl correctly from 3rdparty repo
+require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php';
+require_once __DIR__ . "/classloader.php";
+
+
+class AppTest extends \PHPUnit_Framework_TestCase {
+
+ private $container;
+ private $api;
+ private $controller;
+ private $dispatcher;
+ private $params;
+ private $headers;
+ private $output;
+ private $controllerName;
+ private $controllerMethod;
+
+ protected function setUp() {
+ $this->container = new \Pimple();
+ $this->controller = $this->getMockBuilder(
+ 'OC\AppFramework\Controller\Controller')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->dispatcher = $this->getMockBuilder(
+ 'OC\AppFramework\Http\Dispatcher')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+
+ $this->headers = array('key' => 'value');
+ $this->output = 'hi';
+ $this->controllerName = 'Controller';
+ $this->controllerMethod = 'method';
+
+ $this->container[$this->controllerName] = $this->controller;
+ $this->container['Dispatcher'] = $this->dispatcher;
+ }
+
+
+ public function testControllerNameAndMethodAreBeingPassed(){
+ $return = array(null, array(), null);
+ $this->dispatcher->expects($this->once())
+ ->method('dispatch')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod))
+ ->will($this->returnValue($return));
+
+ $this->expectOutputString('');
+
+ App::main($this->controllerName, $this->controllerMethod, array(),
+ $this->container);
+ }
+
+
+ /*
+ FIXME: this complains about shit headers which are already sent because
+ of the content length. Would be cool if someone could fix this
+
+ public function testOutputIsPrinted(){
+ $return = array(null, array(), $this->output);
+ $this->dispatcher->expects($this->once())
+ ->method('dispatch')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod))
+ ->will($this->returnValue($return));
+
+ $this->expectOutputString($this->output);
+
+ App::main($this->controllerName, $this->controllerMethod, array(),
+ $this->container);
+ }
+ */
+
+ // FIXME: if someone manages to test the headers output, I'd be grateful
+
+
+}
diff --git a/tests/lib/appframework/classloader.php b/tests/lib/appframework/classloader.php
new file mode 100644
index 00000000000..ae485e67b2c
--- /dev/null
+++ b/tests/lib/appframework/classloader.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// to execute without ownCloud, we need to create our own class loader
+spl_autoload_register(function ($className){
+ if (strpos($className, 'OC\\AppFramework') === 0) {
+ $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php');
+ $relPath = __DIR__ . '/../../../lib/' . $path;
+
+ if(file_exists($relPath)){
+ require_once $relPath;
+ }
+ }
+
+ // FIXME: this will most probably not work anymore
+ if (strpos($className, 'OCA\\') === 0) {
+
+ $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php');
+ $relPath = __DIR__ . '/../..' . $path;
+
+ if(file_exists($relPath)){
+ require_once $relPath;
+ }
+ }
+});
diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php
new file mode 100644
index 00000000000..d8357c2a685
--- /dev/null
+++ b/tests/lib/appframework/controller/ControllerTest.php
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace Test\AppFramework\Controller;
+
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Http\JSONResponse;
+use OC\AppFramework\Http\TemplateResponse;
+use OC\AppFramework\Controller\Controller;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class ChildController extends Controller {};
+
+class ControllerTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var Controller
+ */
+ private $controller;
+ private $api;
+
+ protected function setUp(){
+ $request = new Request(
+ array(
+ 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
+ 'post' => array('name' => 'Jane Doe', 'nickname' => 'Janey'),
+ 'urlParams' => array('name' => 'Johnny Weissmüller'),
+ 'files' => array('file' => 'filevalue'),
+ 'env' => array('PATH' => 'daheim'),
+ 'session' => array('sezession' => 'kein'),
+ 'method' => 'hi',
+ )
+ );
+
+ $this->api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName'), array('test'));
+ $this->api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('apptemplate_advanced'));
+
+ $this->controller = new ChildController($this->api, $request);
+ }
+
+
+ public function testParamsGet(){
+ $this->assertEquals('Johnny Weissmüller', $this->controller->params('name', 'Tarzan'));
+ }
+
+
+ public function testParamsGetDefault(){
+ $this->assertEquals('Tarzan', $this->controller->params('Ape Man', 'Tarzan'));
+ }
+
+
+ public function testParamsFile(){
+ $this->assertEquals('filevalue', $this->controller->params('file', 'filevalue'));
+ }
+
+
+ public function testGetUploadedFile(){
+ $this->assertEquals('filevalue', $this->controller->getUploadedFile('file'));
+ }
+
+
+
+ public function testGetUploadedFileDefault(){
+ $this->assertEquals('default', $this->controller->params('files', 'default'));
+ }
+
+
+ public function testGetParams(){
+ $params = array(
+ 'name' => 'Johnny Weissmüller',
+ 'nickname' => 'Janey',
+ );
+
+ $this->assertEquals($params, $this->controller->getParams());
+ }
+
+
+ public function testRender(){
+ $this->assertTrue($this->controller->render('') instanceof TemplateResponse);
+ }
+
+
+ public function testSetParams(){
+ $params = array('john' => 'foo');
+ $response = $this->controller->render('home', $params);
+
+ $this->assertEquals($params, $response->getParams());
+ }
+
+
+ public function testRenderRenderAs(){
+ $ocTpl = $this->getMock('Template', array('fetchPage'));
+ $ocTpl->expects($this->once())
+ ->method('fetchPage');
+
+ $api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName', 'getTemplate'), array('app'));
+ $api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+ $api->expects($this->once())
+ ->method('getTemplate')
+ ->with($this->equalTo('home'), $this->equalTo('admin'), $this->equalTo('app'))
+ ->will($this->returnValue($ocTpl));
+
+ $this->controller = new ChildController($api, new Request());
+ $this->controller->render('home', array(), 'admin')->render();
+ }
+
+
+ public function testRenderHeaders(){
+ $headers = array('one', 'two');
+ $response = $this->controller->render('', array(), '', $headers);
+
+ $this->assertTrue(in_array($headers[0], $response->getHeaders()));
+ $this->assertTrue(in_array($headers[1], $response->getHeaders()));
+ }
+
+
+ public function testGetRequestMethod(){
+ $this->assertEquals('hi', $this->controller->method());
+ }
+
+
+ public function testGetEnvVariable(){
+ $this->assertEquals('daheim', $this->controller->env('PATH'));
+ }
+
+ public function testGetSessionVariable(){
+ $this->assertEquals('kein', $this->controller->session('sezession'));
+ }
+
+
+}
diff --git a/tests/lib/appframework/dependencyinjection/DIContainerTest.php b/tests/lib/appframework/dependencyinjection/DIContainerTest.php
new file mode 100644
index 00000000000..ce346f0a763
--- /dev/null
+++ b/tests/lib/appframework/dependencyinjection/DIContainerTest.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @author Morris Jobke
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ * @copyright 2013 Morris Jobke morris.jobke@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\DependencyInjection;
+
+use \OC\AppFramework\Http\Request;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class DIContainerTest extends \PHPUnit_Framework_TestCase {
+
+ private $container;
+
+ protected function setUp(){
+ $this->container = new DIContainer('name');
+ $this->api = $this->getMock('OC\AppFramework\Core\API', array('getTrans'), array('hi'));
+ }
+
+ private function exchangeAPI(){
+ $this->api->expects($this->any())
+ ->method('getTrans')
+ ->will($this->returnValue('yo'));
+ $this->container['API'] = $this->api;
+ }
+
+ public function testProvidesAPI(){
+ $this->assertTrue(isset($this->container['API']));
+ }
+
+
+ public function testProvidesRequest(){
+ $this->assertTrue(isset($this->container['Request']));
+ }
+
+
+ public function testProvidesSecurityMiddleware(){
+ $this->assertTrue(isset($this->container['SecurityMiddleware']));
+ }
+
+
+ public function testProvidesMiddlewareDispatcher(){
+ $this->assertTrue(isset($this->container['MiddlewareDispatcher']));
+ }
+
+
+ public function testProvidesAppName(){
+ $this->assertTrue(isset($this->container['AppName']));
+ }
+
+
+ public function testAppNameIsSetCorrectly(){
+ $this->assertEquals('name', $this->container['AppName']);
+ }
+
+
+ public function testMiddlewareDispatcherIncludesSecurityMiddleware(){
+ $this->container['Request'] = new Request();
+ $security = $this->container['SecurityMiddleware'];
+ $dispatcher = $this->container['MiddlewareDispatcher'];
+
+ $this->assertContains($security, $dispatcher->getMiddlewares());
+ }
+
+
+ public function testMiddlewareDispatcherDoesNotIncludeTwigWhenTplDirectoryNotSet(){
+ $this->container['Request'] = new Request();
+ $this->exchangeAPI();
+ $dispatcher = $this->container['MiddlewareDispatcher'];
+
+ $this->assertEquals(1, count($dispatcher->getMiddlewares()));
+ }
+
+}
diff --git a/tests/lib/appframework/http/DispatcherTest.php b/tests/lib/appframework/http/DispatcherTest.php
new file mode 100644
index 00000000000..2e3db110504
--- /dev/null
+++ b/tests/lib/appframework/http/DispatcherTest.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+use OC\AppFramework\Core\API;
+use OC\AppFramework\Middleware\MiddlewareDispatcher;
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class DispatcherTest extends \PHPUnit_Framework_TestCase {
+
+
+ private $middlewareDispatcher;
+ private $dispatcher;
+ private $controllerMethod;
+ private $response;
+ private $lastModified;
+ private $etag;
+ private $http;
+
+ protected function setUp() {
+ $this->controllerMethod = 'test';
+
+ $api = $this->getMockBuilder(
+ '\OC\AppFramework\Core\API')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $request = $this->getMockBuilder(
+ '\OC\AppFramework\Http\Request')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->http = $this->getMockBuilder(
+ '\OC\AppFramework\Http\Http')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->middlewareDispatcher = $this->getMockBuilder(
+ '\OC\AppFramework\Middleware\MiddlewareDispatcher')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->controller = $this->getMock(
+ '\OC\AppFramework\Controller\Controller',
+ array($this->controllerMethod), array($api, $request));
+
+ $this->dispatcher = new Dispatcher(
+ $this->http, $this->middlewareDispatcher);
+
+ $this->response = $this->getMockBuilder(
+ '\OC\AppFramework\Http\Response')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->lastModified = new \DateTime(null, new \DateTimeZone('GMT'));
+ $this->etag = 'hi';
+ }
+
+
+ private function setMiddlewareExpections($out=null,
+ $httpHeaders=null, $responseHeaders=array(),
+ $ex=false, $catchEx=true) {
+
+ if($ex) {
+ $exception = new \Exception();
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('beforeController')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod))
+ ->will($this->throwException($exception));
+ if($catchEx) {
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('afterException')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod),
+ $this->equalTo($exception))
+ ->will($this->returnValue($this->response));
+ } else {
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('afterException')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod),
+ $this->equalTo($exception))
+ ->will($this->returnValue(null));
+ return;
+ }
+ } else {
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('beforeController')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod));
+ $this->controller->expects($this->once())
+ ->method($this->controllerMethod)
+ ->will($this->returnValue($this->response));
+ }
+
+ $this->response->expects($this->once())
+ ->method('render')
+ ->will($this->returnValue($out));
+ $this->response->expects($this->once())
+ ->method('getStatus')
+ ->will($this->returnValue(Http::STATUS_OK));
+ $this->response->expects($this->once())
+ ->method('getLastModified')
+ ->will($this->returnValue($this->lastModified));
+ $this->response->expects($this->once())
+ ->method('getETag')
+ ->will($this->returnValue($this->etag));
+ $this->response->expects($this->once())
+ ->method('getHeaders')
+ ->will($this->returnValue($responseHeaders));
+ $this->http->expects($this->once())
+ ->method('getStatusHeader')
+ ->with($this->equalTo(Http::STATUS_OK),
+ $this->equalTo($this->lastModified),
+ $this->equalTo($this->etag))
+ ->will($this->returnValue($httpHeaders));
+
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('afterController')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod),
+ $this->equalTo($this->response))
+ ->will($this->returnValue($this->response));
+
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('afterController')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod),
+ $this->equalTo($this->response))
+ ->will($this->returnValue($this->response));
+
+ $this->middlewareDispatcher->expects($this->once())
+ ->method('beforeOutput')
+ ->with($this->equalTo($this->controller),
+ $this->equalTo($this->controllerMethod),
+ $this->equalTo($out))
+ ->will($this->returnValue($out));
+
+
+ }
+
+
+ public function testDispatcherReturnsArrayWith2Entries() {
+ $this->setMiddlewareExpections();
+
+ $response = $this->dispatcher->dispatch($this->controller,
+ $this->controllerMethod);
+ $this->assertNull($response[0]);
+ $this->assertEquals(array(), $response[1]);
+ $this->assertNull($response[2]);
+ }
+
+
+ public function testHeadersAndOutputAreReturned(){
+ $out = 'yo';
+ $httpHeaders = 'Http';
+ $responseHeaders = array('hell' => 'yeah');
+ $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders);
+
+ $response = $this->dispatcher->dispatch($this->controller,
+ $this->controllerMethod);
+
+ $this->assertEquals($httpHeaders, $response[0]);
+ $this->assertEquals($responseHeaders, $response[1]);
+ $this->assertEquals($out, $response[2]);
+ }
+
+
+ public function testExceptionCallsAfterException() {
+ $out = 'yo';
+ $httpHeaders = 'Http';
+ $responseHeaders = array('hell' => 'yeah');
+ $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true);
+
+ $response = $this->dispatcher->dispatch($this->controller,
+ $this->controllerMethod);
+
+ $this->assertEquals($httpHeaders, $response[0]);
+ $this->assertEquals($responseHeaders, $response[1]);
+ $this->assertEquals($out, $response[2]);
+ }
+
+
+ public function testExceptionThrowsIfCanNotBeHandledByAfterException() {
+ $out = 'yo';
+ $httpHeaders = 'Http';
+ $responseHeaders = array('hell' => 'yeah');
+ $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true, false);
+
+ $this->setExpectedException('\Exception');
+ $response = $this->dispatcher->dispatch($this->controller,
+ $this->controllerMethod);
+
+ }
+
+}
diff --git a/tests/lib/appframework/http/DownloadResponseTest.php b/tests/lib/appframework/http/DownloadResponseTest.php
new file mode 100644
index 00000000000..103cfe7588c
--- /dev/null
+++ b/tests/lib/appframework/http/DownloadResponseTest.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class ChildDownloadResponse extends DownloadResponse {};
+
+
+class DownloadResponseTest extends \PHPUnit_Framework_TestCase {
+
+ protected $response;
+
+ protected function setUp(){
+ $this->response = new ChildDownloadResponse('file', 'content');
+ }
+
+
+ public function testHeaders() {
+ $headers = $this->response->getHeaders();
+
+ $this->assertContains('attachment; filename="file"', $headers['Content-Disposition']);
+ $this->assertContains('content', $headers['Content-Type']);
+ }
+
+
+}
diff --git a/tests/lib/appframework/http/HttpTest.php b/tests/lib/appframework/http/HttpTest.php
new file mode 100644
index 00000000000..306bc3caf42
--- /dev/null
+++ b/tests/lib/appframework/http/HttpTest.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+
+class HttpTest extends \PHPUnit_Framework_TestCase {
+
+ private $server;
+ private $http;
+
+ protected function setUp(){
+ $this->server = array();
+ $this->http = new Http($this->server);
+ }
+
+
+ public function testProtocol() {
+ $header = $this->http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT);
+ $this->assertEquals('HTTP/1.1 307 Temporary Redirect', $header);
+ }
+
+
+ public function testProtocol10() {
+ $this->http = new Http($this->server, 'HTTP/1.0');
+ $header = $this->http->getStatusHeader(Http::STATUS_OK);
+ $this->assertEquals('HTTP/1.0 200 OK', $header);
+ }
+
+
+ public function testEtagMatchReturnsNotModified() {
+ $http = new Http(array('HTTP_IF_NONE_MATCH' => 'hi'));
+
+ $header = $http->getStatusHeader(Http::STATUS_OK, null, 'hi');
+ $this->assertEquals('HTTP/1.1 304 Not Modified', $header);
+ }
+
+
+ public function testLastModifiedMatchReturnsNotModified() {
+ $dateTime = new \DateTime(null, new \DateTimeZone('GMT'));
+ $dateTime->setTimestamp('12');
+
+ $http = new Http(
+ array(
+ 'HTTP_IF_MODIFIED_SINCE' => 'Thu, 01 Jan 1970 00:00:12 +0000')
+ );
+
+ $header = $http->getStatusHeader(Http::STATUS_OK, $dateTime);
+ $this->assertEquals('HTTP/1.1 304 Not Modified', $header);
+ }
+
+
+
+ public function testTempRedirectBecomesFoundInHttp10() {
+ $http = new Http(array(), 'HTTP/1.0');
+
+ $header = $http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT);
+ $this->assertEquals('HTTP/1.0 302 Found', $header);
+ }
+ // TODO: write unittests for http codes
+
+}
diff --git a/tests/lib/appframework/http/JSONResponseTest.php b/tests/lib/appframework/http/JSONResponseTest.php
new file mode 100644
index 00000000000..d15e08f6ce1
--- /dev/null
+++ b/tests/lib/appframework/http/JSONResponseTest.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @author Morris Jobke
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ * @copyright 2013 Morris Jobke morris.jobke@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+
+class JSONResponseTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var JSONResponse
+ */
+ private $json;
+
+ protected function setUp() {
+ $this->json = new JSONResponse();
+ }
+
+
+ public function testHeader() {
+ $headers = $this->json->getHeaders();
+ $this->assertEquals('application/json; charset=utf-8', $headers['Content-type']);
+ }
+
+
+ public function testSetData() {
+ $params = array('hi', 'yo');
+ $this->json->setData($params);
+
+ $this->assertEquals(array('hi', 'yo'), $this->json->getData());
+ }
+
+
+ public function testSetRender() {
+ $params = array('test' => 'hi');
+ $this->json->setData($params);
+
+ $expected = '{"test":"hi"}';
+
+ $this->assertEquals($expected, $this->json->render());
+ }
+
+
+ public function testRender() {
+ $params = array('test' => 'hi');
+ $this->json->setData($params);
+
+ $expected = '{"test":"hi"}';
+
+ $this->assertEquals($expected, $this->json->render());
+ }
+
+
+ public function testShouldHaveXContentHeaderByDefault() {
+ $headers = $this->json->getHeaders();
+ $this->assertEquals('nosniff', $headers['X-Content-Type-Options']);
+ }
+
+
+ public function testConstructorAllowsToSetData() {
+ $data = array('hi');
+ $code = 300;
+ $response = new JSONResponse($data, $code);
+
+ $expected = '["hi"]';
+ $this->assertEquals($expected, $response->render());
+ $this->assertEquals($code, $response->getStatus());
+ }
+
+}
diff --git a/tests/lib/appframework/http/RedirectResponseTest.php b/tests/lib/appframework/http/RedirectResponseTest.php
new file mode 100644
index 00000000000..a8577feed29
--- /dev/null
+++ b/tests/lib/appframework/http/RedirectResponseTest.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+
+class RedirectResponseTest extends \PHPUnit_Framework_TestCase {
+
+
+ protected $response;
+
+ protected function setUp(){
+ $this->response = new RedirectResponse('/url');
+ }
+
+
+ public function testHeaders() {
+ $headers = $this->response->getHeaders();
+ $this->assertEquals('/url', $headers['Location']);
+ $this->assertEquals(Http::STATUS_TEMPORARY_REDIRECT,
+ $this->response->getStatus());
+ }
+
+
+ public function testGetRedirectUrl(){
+ $this->assertEquals('/url', $this->response->getRedirectUrl());
+ }
+
+
+}
diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php
new file mode 100644
index 00000000000..c1f56c01636
--- /dev/null
+++ b/tests/lib/appframework/http/RequestTest.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net)
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+class RequestTest extends \PHPUnit_Framework_TestCase {
+
+ public function testRequestAccessors() {
+ $vars = array(
+ 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
+ );
+
+ $request = new Request($vars);
+
+ // Countable
+ $this->assertEquals(2, count($request));
+ // Array access
+ $this->assertEquals('Joey', $request['nickname']);
+ // "Magic" accessors
+ $this->assertEquals('Joey', $request->{'nickname'});
+ $this->assertTrue(isset($request['nickname']));
+ $this->assertTrue(isset($request->{'nickname'}));
+ $this->assertEquals(false, isset($request->{'flickname'}));
+ // Only testing 'get', but same approach for post, files etc.
+ $this->assertEquals('Joey', $request->get['nickname']);
+ // Always returns null if variable not set.
+ $this->assertEquals(null, $request->{'flickname'});
+ }
+
+ // urlParams has precedence over POST which has precedence over GET
+ public function testPrecedence() {
+ $vars = array(
+ 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
+ 'post' => array('name' => 'Jane Doe', 'nickname' => 'Janey'),
+ 'urlParams' => array('user' => 'jw', 'name' => 'Johnny Weissmüller'),
+ );
+
+ $request = new Request($vars);
+
+ $this->assertEquals(3, count($request));
+ $this->assertEquals('Janey', $request->{'nickname'});
+ $this->assertEquals('Johnny Weissmüller', $request->{'name'});
+ }
+
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testImmutableArrayAccess() {
+ $vars = array(
+ 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
+ );
+
+ $request = new Request($vars);
+ $request['nickname'] = 'Janey';
+ }
+
+ /**
+ * @expectedException RuntimeException
+ */
+ public function testImmutableMagicAccess() {
+ $vars = array(
+ 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
+ );
+
+ $request = new Request($vars);
+ $request->{'nickname'} = 'Janey';
+ }
+
+}
diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php
new file mode 100644
index 00000000000..621ba66545f
--- /dev/null
+++ b/tests/lib/appframework/http/ResponseTest.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+
+class ResponseTest extends \PHPUnit_Framework_TestCase {
+
+
+ private $childResponse;
+
+ protected function setUp(){
+ $this->childResponse = new Response();
+ }
+
+
+ public function testAddHeader(){
+ $this->childResponse->addHeader('hello', 'world');
+ $headers = $this->childResponse->getHeaders();
+ $this->assertEquals('world', $headers['hello']);
+ }
+
+
+ public function testAddHeaderValueNullDeletesIt(){
+ $this->childResponse->addHeader('hello', 'world');
+ $this->childResponse->addHeader('hello', null);
+ $this->assertEquals(1, count($this->childResponse->getHeaders()));
+ }
+
+
+ public function testCacheHeadersAreDisabledByDefault(){
+ $headers = $this->childResponse->getHeaders();
+ $this->assertEquals('no-cache, must-revalidate', $headers['Cache-Control']);
+ }
+
+
+ public function testRenderReturnNullByDefault(){
+ $this->assertEquals(null, $this->childResponse->render());
+ }
+
+
+ public function testGetStatus() {
+ $default = $this->childResponse->getStatus();
+
+ $this->childResponse->setStatus(Http::STATUS_NOT_FOUND);
+
+ $this->assertEquals(Http::STATUS_OK, $default);
+ $this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus());
+ }
+
+
+ public function testGetEtag() {
+ $this->childResponse->setEtag('hi');
+ $this->assertEquals('hi', $this->childResponse->getEtag());
+ }
+
+
+ public function testGetLastModified() {
+ $lastModified = new \DateTime(null, new \DateTimeZone('GMT'));
+ $lastModified->setTimestamp(1);
+ $this->childResponse->setLastModified($lastModified);
+ $this->assertEquals($lastModified, $this->childResponse->getLastModified());
+ }
+
+
+
+ public function testCacheSecondsZero() {
+ $this->childResponse->cacheFor(0);
+
+ $headers = $this->childResponse->getHeaders();
+ $this->assertEquals('no-cache, must-revalidate', $headers['Cache-Control']);
+ }
+
+
+ public function testCacheSeconds() {
+ $this->childResponse->cacheFor(33);
+
+ $headers = $this->childResponse->getHeaders();
+ $this->assertEquals('max-age=33, must-revalidate',
+ $headers['Cache-Control']);
+ }
+
+
+
+ public function testEtagLastModifiedHeaders() {
+ $lastModified = new \DateTime(null, new \DateTimeZone('GMT'));
+ $lastModified->setTimestamp(1);
+ $this->childResponse->setLastModified($lastModified);
+ $headers = $this->childResponse->getHeaders();
+ $this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']);
+ }
+
+
+}
diff --git a/tests/lib/appframework/http/TemplateResponseTest.php b/tests/lib/appframework/http/TemplateResponseTest.php
new file mode 100644
index 00000000000..30684725b72
--- /dev/null
+++ b/tests/lib/appframework/http/TemplateResponseTest.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Http;
+
+use OC\AppFramework\Core\API;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class TemplateResponseTest extends \PHPUnit_Framework_TestCase {
+
+ private $tpl;
+ private $api;
+
+ protected function setUp() {
+ $this->api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName'), array('test'));
+ $this->api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+
+ $this->tpl = new TemplateResponse($this->api, 'home');
+ }
+
+
+ public function testSetParams(){
+ $params = array('hi' => 'yo');
+ $this->tpl->setParams($params);
+
+ $this->assertEquals(array('hi' => 'yo'), $this->tpl->getParams());
+ }
+
+
+ public function testGetTemplateName(){
+ $this->assertEquals('home', $this->tpl->getTemplateName());
+ }
+
+
+ public function testRender(){
+ $ocTpl = $this->getMock('Template', array('fetchPage'));
+ $ocTpl->expects($this->once())
+ ->method('fetchPage');
+
+ $api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName', 'getTemplate'), array('app'));
+ $api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+ $api->expects($this->once())
+ ->method('getTemplate')
+ ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app'))
+ ->will($this->returnValue($ocTpl));
+
+ $tpl = new TemplateResponse($api, 'home');
+
+ $tpl->render();
+ }
+
+
+ public function testRenderAssignsParams(){
+ $params = array('john' => 'doe');
+
+ $ocTpl = $this->getMock('Template', array('assign', 'fetchPage'));
+ $ocTpl->expects($this->once())
+ ->method('assign')
+ ->with($this->equalTo('john'), $this->equalTo('doe'));
+
+ $api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName', 'getTemplate'), array('app'));
+ $api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+ $api->expects($this->once())
+ ->method('getTemplate')
+ ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app'))
+ ->will($this->returnValue($ocTpl));
+
+ $tpl = new TemplateResponse($api, 'home');
+ $tpl->setParams($params);
+
+ $tpl->render();
+ }
+
+
+ public function testRenderDifferentApp(){
+ $ocTpl = $this->getMock('Template', array('fetchPage'));
+ $ocTpl->expects($this->once())
+ ->method('fetchPage');
+
+ $api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName', 'getTemplate'), array('app'));
+ $api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+ $api->expects($this->once())
+ ->method('getTemplate')
+ ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app2'))
+ ->will($this->returnValue($ocTpl));
+
+ $tpl = new TemplateResponse($api, 'home', 'app2');
+
+ $tpl->render();
+ }
+
+
+ public function testRenderDifferentRenderAs(){
+ $ocTpl = $this->getMock('Template', array('fetchPage'));
+ $ocTpl->expects($this->once())
+ ->method('fetchPage');
+
+ $api = $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName', 'getTemplate'), array('app'));
+ $api->expects($this->any())
+ ->method('getAppName')
+ ->will($this->returnValue('app'));
+ $api->expects($this->once())
+ ->method('getTemplate')
+ ->with($this->equalTo('home'), $this->equalTo('admin'), $this->equalTo('app'))
+ ->will($this->returnValue($ocTpl));
+
+ $tpl = new TemplateResponse($api, 'home');
+ $tpl->renderAs('admin');
+
+ $tpl->render();
+ }
+
+
+ public function testGetRenderAs(){
+ $render = 'myrender';
+ $this->tpl->renderAs($render);
+ $this->assertEquals($render, $this->tpl->getRenderAs());
+ }
+
+}
diff --git a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php
new file mode 100644
index 00000000000..bfa54a48ea3
--- /dev/null
+++ b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework;
+
+use OC\AppFramework\Controller\Controller;
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Http\Response;
+use OC\AppFramework\Middleware\Middleware;
+use OC\AppFramework\Middleware\MiddlewareDispatcher;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+// needed to test ordering
+class TestMiddleware extends Middleware {
+ public static $beforeControllerCalled = 0;
+ public static $afterControllerCalled = 0;
+ public static $afterExceptionCalled = 0;
+ public static $beforeOutputCalled = 0;
+
+ public $beforeControllerOrder = 0;
+ public $afterControllerOrder = 0;
+ public $afterExceptionOrder = 0;
+ public $beforeOutputOrder = 0;
+
+ public $controller;
+ public $methodName;
+ public $exception;
+ public $response;
+ public $output;
+
+ private $beforeControllerThrowsEx;
+
+ public function __construct($beforeControllerThrowsEx) {
+ self::$beforeControllerCalled = 0;
+ self::$afterControllerCalled = 0;
+ self::$afterExceptionCalled = 0;
+ self::$beforeOutputCalled = 0;
+ $this->beforeControllerThrowsEx = $beforeControllerThrowsEx;
+ }
+
+ public function beforeController($controller, $methodName){
+ self::$beforeControllerCalled++;
+ $this->beforeControllerOrder = self::$beforeControllerCalled;
+ $this->controller = $controller;
+ $this->methodName = $methodName;
+ if($this->beforeControllerThrowsEx){
+ throw new \Exception();
+ }
+ }
+
+ public function afterException($controller, $methodName, \Exception $exception){
+ self::$afterExceptionCalled++;
+ $this->afterExceptionOrder = self::$afterExceptionCalled;
+ $this->controller = $controller;
+ $this->methodName = $methodName;
+ $this->exception = $exception;
+ parent::afterException($controller, $methodName, $exception);
+ }
+
+ public function afterController($controller, $methodName, Response $response){
+ self::$afterControllerCalled++;
+ $this->afterControllerOrder = self::$afterControllerCalled;
+ $this->controller = $controller;
+ $this->methodName = $methodName;
+ $this->response = $response;
+ return parent::afterController($controller, $methodName, $response);
+ }
+
+ public function beforeOutput($controller, $methodName, $output){
+ self::$beforeOutputCalled++;
+ $this->beforeOutputOrder = self::$beforeOutputCalled;
+ $this->controller = $controller;
+ $this->methodName = $methodName;
+ $this->output = $output;
+ return parent::beforeOutput($controller, $methodName, $output);
+ }
+}
+
+
+class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase {
+
+ private $dispatcher;
+
+
+ public function setUp() {
+ $this->dispatcher = new MiddlewareDispatcher();
+ $this->controller = $this->getControllerMock();
+ $this->method = 'method';
+ $this->response = new Response();
+ $this->output = 'hi';
+ $this->exception = new \Exception();
+ }
+
+
+ private function getAPIMock(){
+ return $this->getMock('OC\AppFramework\Core\API',
+ array('getAppName'), array('app'));
+ }
+
+
+ private function getControllerMock(){
+ return $this->getMock('OC\AppFramework\Controller\Controller', array('method'),
+ array($this->getAPIMock(), new Request()));
+ }
+
+
+ private function getMiddleware($beforeControllerThrowsEx=false){
+ $m1 = new TestMiddleware($beforeControllerThrowsEx);
+ $this->dispatcher->registerMiddleware($m1);
+ return $m1;
+ }
+
+
+ public function testAfterExceptionShouldReturnResponseOfMiddleware(){
+ $response = new Response();
+ $m1 = $this->getMock('\OC\AppFramework\Middleware\Middleware',
+ array('afterException', 'beforeController'));
+ $m1->expects($this->never())
+ ->method('afterException');
+
+ $m2 = $this->getMock('OC\AppFramework\Middleware\Middleware',
+ array('afterException', 'beforeController'));
+ $m2->expects($this->once())
+ ->method('afterException')
+ ->will($this->returnValue($response));
+
+ $this->dispatcher->registerMiddleware($m1);
+ $this->dispatcher->registerMiddleware($m2);
+
+ $this->dispatcher->beforeController($this->controller, $this->method);
+ $this->assertEquals($response, $this->dispatcher->afterException($this->controller, $this->method, $this->exception));
+ }
+
+
+ public function testAfterExceptionShouldThrowAgainWhenNotHandled(){
+ $m1 = new TestMiddleware(false);
+ $m2 = new TestMiddleware(true);
+
+ $this->dispatcher->registerMiddleware($m1);
+ $this->dispatcher->registerMiddleware($m2);
+
+ $this->setExpectedException('\Exception');
+ $this->dispatcher->beforeController($this->controller, $this->method);
+ $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
+ }
+
+
+ public function testBeforeControllerCorrectArguments(){
+ $m1 = $this->getMiddleware();
+ $this->dispatcher->beforeController($this->controller, $this->method);
+
+ $this->assertEquals($this->controller, $m1->controller);
+ $this->assertEquals($this->method, $m1->methodName);
+ }
+
+
+ public function testAfterControllerCorrectArguments(){
+ $m1 = $this->getMiddleware();
+
+ $this->dispatcher->afterController($this->controller, $this->method, $this->response);
+
+ $this->assertEquals($this->controller, $m1->controller);
+ $this->assertEquals($this->method, $m1->methodName);
+ $this->assertEquals($this->response, $m1->response);
+ }
+
+
+ public function testAfterExceptionCorrectArguments(){
+ $m1 = $this->getMiddleware();
+
+ $this->setExpectedException('\Exception');
+
+ $this->dispatcher->beforeController($this->controller, $this->method);
+ $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
+
+ $this->assertEquals($this->controller, $m1->controller);
+ $this->assertEquals($this->method, $m1->methodName);
+ $this->assertEquals($this->exception, $m1->exception);
+ }
+
+
+ public function testBeforeOutputCorrectArguments(){
+ $m1 = $this->getMiddleware();
+
+ $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output);
+
+ $this->assertEquals($this->controller, $m1->controller);
+ $this->assertEquals($this->method, $m1->methodName);
+ $this->assertEquals($this->output, $m1->output);
+ }
+
+
+ public function testBeforeControllerOrder(){
+ $m1 = $this->getMiddleware();
+ $m2 = $this->getMiddleware();
+
+ $this->dispatcher->beforeController($this->controller, $this->method);
+
+ $this->assertEquals(1, $m1->beforeControllerOrder);
+ $this->assertEquals(2, $m2->beforeControllerOrder);
+ }
+
+ public function testAfterControllerOrder(){
+ $m1 = $this->getMiddleware();
+ $m2 = $this->getMiddleware();
+
+ $this->dispatcher->afterController($this->controller, $this->method, $this->response);
+
+ $this->assertEquals(2, $m1->afterControllerOrder);
+ $this->assertEquals(1, $m2->afterControllerOrder);
+ }
+
+
+ public function testAfterExceptionOrder(){
+ $m1 = $this->getMiddleware();
+ $m2 = $this->getMiddleware();
+
+ $this->setExpectedException('\Exception');
+ $this->dispatcher->beforeController($this->controller, $this->method);
+ $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
+
+ $this->assertEquals(1, $m1->afterExceptionOrder);
+ $this->assertEquals(1, $m2->afterExceptionOrder);
+ }
+
+
+ public function testBeforeOutputOrder(){
+ $m1 = $this->getMiddleware();
+ $m2 = $this->getMiddleware();
+
+ $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output);
+
+ $this->assertEquals(2, $m1->beforeOutputOrder);
+ $this->assertEquals(1, $m2->beforeOutputOrder);
+ }
+
+
+ public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares(){
+ $m1 = $this->getMiddleware();
+ $m2 = $this->getMiddleware(true);
+ $m3 = $this->getMock('\OC\AppFramework\Middleware\Middleware');
+ $m3->expects($this->never())
+ ->method('afterException');
+ $m3->expects($this->never())
+ ->method('beforeController');
+ $m3->expects($this->never())
+ ->method('afterController');
+
+ $this->dispatcher->registerMiddleware($m3);
+
+ $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output);
+
+ $this->assertEquals(2, $m1->beforeOutputOrder);
+ $this->assertEquals(1, $m2->beforeOutputOrder);
+ }
+}
diff --git a/tests/lib/appframework/middleware/MiddlewareTest.php b/tests/lib/appframework/middleware/MiddlewareTest.php
new file mode 100644
index 00000000000..1adce6b3d4f
--- /dev/null
+++ b/tests/lib/appframework/middleware/MiddlewareTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework;
+
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Middleware\Middleware;
+
+
+require_once(__DIR__ . "/../classloader.php");
+
+
+class ChildMiddleware extends Middleware {};
+
+
+class MiddlewareTest extends \PHPUnit_Framework_TestCase {
+
+ private $middleware;
+ private $controller;
+ private $exception;
+ private $api;
+
+ protected function setUp(){
+ $this->middleware = new ChildMiddleware();
+
+ $this->api = $this->getMock('OC\AppFramework\Core\API',
+ array(), array('test'));
+
+ $this->controller = $this->getMock('OC\AppFramework\Controller\Controller',
+ array(), array($this->api, new Request()));
+ $this->exception = new \Exception();
+ $this->response = $this->getMock('OC\AppFramework\Http\Response');
+ }
+
+
+ public function testBeforeController() {
+ $this->middleware->beforeController($this->controller, null, $this->exception);
+ }
+
+
+ public function testAfterExceptionRaiseAgainWhenUnhandled() {
+ $this->setExpectedException('Exception');
+ $afterEx = $this->middleware->afterException($this->controller, null, $this->exception);
+ }
+
+
+ public function testAfterControllerReturnResponseWhenUnhandled() {
+ $response = $this->middleware->afterController($this->controller, null, $this->response);
+
+ $this->assertEquals($this->response, $response);
+ }
+
+
+ public function testBeforeOutputReturnOutputhenUnhandled() {
+ $output = $this->middleware->beforeOutput($this->controller, null, 'test');
+
+ $this->assertEquals('test', $output);
+ }
+
+
+}
diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php
new file mode 100644
index 00000000000..0b2103564e4
--- /dev/null
+++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php
@@ -0,0 +1,388 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Middleware\Security;
+
+use OC\AppFramework\Http\Http;
+use OC\AppFramework\Http\Request;
+use OC\AppFramework\Http\RedirectResponse;
+use OC\AppFramework\Http\JSONResponse;
+use OC\AppFramework\Middleware\Middleware;
+
+
+require_once(__DIR__ . "/../../classloader.php");
+
+
+class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase {
+
+ private $middleware;
+ private $controller;
+ private $secException;
+ private $secAjaxException;
+ private $request;
+
+ public function setUp() {
+ $api = $this->getMock('OC\AppFramework\Core\API', array(), array('test'));
+ $this->controller = $this->getMock('OC\AppFramework\Controller\Controller',
+ array(), array($api, new Request()));
+
+ $this->request = new Request();
+ $this->middleware = new SecurityMiddleware($api, $this->request);
+ $this->secException = new SecurityException('hey', false);
+ $this->secAjaxException = new SecurityException('hey', true);
+ }
+
+
+ private function getAPI(){
+ return $this->getMock('OC\AppFramework\Core\API',
+ array('isLoggedIn', 'passesCSRFCheck', 'isAdminUser',
+ 'isSubAdminUser', 'activateNavigationEntry',
+ 'getUserId'),
+ array('app'));
+ }
+
+
+ private function checkNavEntry($method, $shouldBeActivated=false){
+ $api = $this->getAPI();
+
+ if($shouldBeActivated){
+ $api->expects($this->once())
+ ->method('activateNavigationEntry');
+ } else {
+ $api->expects($this->never())
+ ->method('activateNavigationEntry');
+ }
+
+ $sec = new SecurityMiddleware($api, $this->request);
+ $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testSetNavigationEntry(){
+ $this->checkNavEntry('testSetNavigationEntry', true);
+ }
+
+
+ private function ajaxExceptionCheck($method, $shouldBeAjax=false){
+ $api = $this->getAPI();
+ $api->expects($this->any())
+ ->method('passesCSRFCheck')
+ ->will($this->returnValue(false));
+
+ $sec = new SecurityMiddleware($api, $this->request);
+
+ try {
+ $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest',
+ $method);
+ } catch (SecurityException $ex){
+ if($shouldBeAjax){
+ $this->assertTrue($ex->isAjax());
+ } else {
+ $this->assertFalse($ex->isAjax());
+ }
+
+ }
+ }
+
+
+ /**
+ * @Ajax
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testAjaxException(){
+ $this->ajaxExceptionCheck('testAjaxException');
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testNoAjaxException(){
+ $this->ajaxExceptionCheck('testNoAjaxException');
+ }
+
+
+ private function ajaxExceptionStatus($method, $test, $status) {
+ $api = $this->getAPI();
+ $api->expects($this->any())
+ ->method($test)
+ ->will($this->returnValue(false));
+
+ $sec = new SecurityMiddleware($api, $this->request);
+
+ try {
+ $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest',
+ $method);
+ } catch (SecurityException $ex){
+ $this->assertEquals($status, $ex->getCode());
+ }
+ }
+
+ /**
+ * @Ajax
+ */
+ public function testAjaxStatusLoggedInCheck() {
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusLoggedInCheck',
+ 'isLoggedIn',
+ Http::STATUS_UNAUTHORIZED
+ );
+ }
+
+ /**
+ * @Ajax
+ * @IsLoggedInExemption
+ */
+ public function testAjaxNotAdminCheck() {
+ $this->ajaxExceptionStatus(
+ 'testAjaxNotAdminCheck',
+ 'isAdminUser',
+ Http::STATUS_FORBIDDEN
+ );
+ }
+
+ /**
+ * @Ajax
+ * @IsLoggedInExemption
+ * @IsAdminExemption
+ */
+ public function testAjaxNotSubAdminCheck() {
+ $this->ajaxExceptionStatus(
+ 'testAjaxNotSubAdminCheck',
+ 'isSubAdminUser',
+ Http::STATUS_FORBIDDEN
+ );
+ }
+
+ /**
+ * @Ajax
+ * @IsLoggedInExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testAjaxStatusCSRFCheck() {
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusCSRFCheck',
+ 'passesCSRFCheck',
+ Http::STATUS_PRECONDITION_FAILED
+ );
+ }
+
+ /**
+ * @Ajax
+ * @CSRFExemption
+ * @IsLoggedInExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testAjaxStatusAllGood() {
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusAllGood',
+ 'isLoggedIn',
+ 0
+ );
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusAllGood',
+ 'isAdminUser',
+ 0
+ );
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusAllGood',
+ 'isSubAdminUser',
+ 0
+ );
+ $this->ajaxExceptionStatus(
+ 'testAjaxStatusAllGood',
+ 'passesCSRFCheck',
+ 0
+ );
+ }
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testNoChecks(){
+ $api = $this->getAPI();
+ $api->expects($this->never())
+ ->method('passesCSRFCheck')
+ ->will($this->returnValue(true));
+ $api->expects($this->never())
+ ->method('isAdminUser')
+ ->will($this->returnValue(true));
+ $api->expects($this->never())
+ ->method('isSubAdminUser')
+ ->will($this->returnValue(true));
+ $api->expects($this->never())
+ ->method('isLoggedIn')
+ ->will($this->returnValue(true));
+
+ $sec = new SecurityMiddleware($api, $this->request);
+ $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest',
+ 'testNoChecks');
+ }
+
+
+ private function securityCheck($method, $expects, $shouldFail=false){
+ $api = $this->getAPI();
+ $api->expects($this->once())
+ ->method($expects)
+ ->will($this->returnValue(!$shouldFail));
+
+ $sec = new SecurityMiddleware($api, $this->request);
+
+ if($shouldFail){
+ $this->setExpectedException('\OC\AppFramework\Middleware\Security\SecurityException');
+ }
+
+ $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method);
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testCsrfCheck(){
+ $this->securityCheck('testCsrfCheck', 'passesCSRFCheck');
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testFailCsrfCheck(){
+ $this->securityCheck('testFailCsrfCheck', 'passesCSRFCheck', true);
+ }
+
+
+ /**
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testLoggedInCheck(){
+ $this->securityCheck('testLoggedInCheck', 'isLoggedIn');
+ }
+
+
+ /**
+ * @CSRFExemption
+ * @IsAdminExemption
+ * @IsSubAdminExemption
+ */
+ public function testFailLoggedInCheck(){
+ $this->securityCheck('testFailLoggedInCheck', 'isLoggedIn', true);
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsSubAdminExemption
+ */
+ public function testIsAdminCheck(){
+ $this->securityCheck('testIsAdminCheck', 'isAdminUser');
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsSubAdminExemption
+ */
+ public function testFailIsAdminCheck(){
+ $this->securityCheck('testFailIsAdminCheck', 'isAdminUser', true);
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ */
+ public function testIsSubAdminCheck(){
+ $this->securityCheck('testIsSubAdminCheck', 'isSubAdminUser');
+ }
+
+
+ /**
+ * @IsLoggedInExemption
+ * @CSRFExemption
+ * @IsAdminExemption
+ */
+ public function testFailIsSubAdminCheck(){
+ $this->securityCheck('testFailIsSubAdminCheck', 'isSubAdminUser', true);
+ }
+
+
+
+ public function testAfterExceptionNotCaughtThrowsItAgain(){
+ $ex = new \Exception();
+ $this->setExpectedException('\Exception');
+ $this->middleware->afterException($this->controller, 'test', $ex);
+ }
+
+
+ public function testAfterExceptionReturnsRedirect(){
+ $api = $this->getMock('OC\AppFramework\Core\API', array(), array('test'));
+ $this->controller = $this->getMock('OC\AppFramework\Controller\Controller',
+ array(), array($api, new Request()));
+
+ $this->request = new Request(
+ array('server' => array('HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8')));
+ $this->middleware = new SecurityMiddleware($api, $this->request);
+ $response = $this->middleware->afterException($this->controller, 'test',
+ $this->secException);
+
+ $this->assertTrue($response instanceof RedirectResponse);
+ }
+
+
+ public function testAfterAjaxExceptionReturnsJSONError(){
+ $response = $this->middleware->afterException($this->controller, 'test',
+ $this->secAjaxException);
+
+ $this->assertTrue($response instanceof JSONResponse);
+ }
+
+
+}
diff --git a/tests/lib/appframework/routing/RoutingTest.php b/tests/lib/appframework/routing/RoutingTest.php
new file mode 100644
index 00000000000..92ad461471d
--- /dev/null
+++ b/tests/lib/appframework/routing/RoutingTest.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace OC\AppFramework\Routing;
+
+use OC\AppFramework\DependencyInjection\DIContainer;
+use OC\AppFramework\routing\RouteConfig;
+
+require_once(__DIR__ . "/../classloader.php");
+
+class RouteConfigTest extends \PHPUnit_Framework_TestCase
+{
+
+ public function testSimpleRoute()
+ {
+ $routes = array('routes' => array(
+ array('name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET')
+ ));
+
+ $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/folders/{folderId}/open', 'FoldersController', 'open');
+ }
+
+ public function testSimpleRouteWithMissingVerb()
+ {
+ $routes = array('routes' => array(
+ array('name' => 'folders#open', 'url' => '/folders/{folderId}/open')
+ ));
+
+ $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/folders/{folderId}/open', 'FoldersController', 'open');
+ }
+
+ public function testSimpleRouteWithLowercaseVerb()
+ {
+ $routes = array('routes' => array(
+ array('name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete')
+ ));
+
+ $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/folders/{folderId}/open', 'FoldersController', 'open');
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ */
+ public function testSimpleRouteWithBrokenName()
+ {
+ $routes = array('routes' => array(
+ array('name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete')
+ ));
+
+ // router mock
+ $router = $this->getMock("\OC_Router", array('create'));
+
+ // load route configuration
+ $container = new DIContainer('app1');
+ $config = new RouteConfig($container, $router, $routes);
+
+ $config->register();
+ }
+
+ public function testSimpleRouteWithUnderScoreNames()
+ {
+ $routes = array('routes' => array(
+ array('name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete')
+ ));
+
+ $this->assertSimpleRoute($routes, 'admin_folders.open_current', 'DELETE', '/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent');
+ }
+
+ public function testResource()
+ {
+ $routes = array('resources' => array('accounts' => array('url' => '/accounts')));
+
+ $this->assertResource($routes, 'accounts', '/accounts', 'AccountsController', 'accountId');
+ }
+
+ public function testResourceWithUnderScoreName()
+ {
+ $routes = array('resources' => array('admin_accounts' => array('url' => '/admin/accounts')));
+
+ $this->assertResource($routes, 'admin_accounts', '/admin/accounts', 'AdminAccountsController', 'adminAccountId');
+ }
+
+ private function assertSimpleRoute($routes, $name, $verb, $url, $controllerName, $actionName)
+ {
+ // route mocks
+ $route = $this->mockRoute($verb, $controllerName, $actionName);
+
+ // router mock
+ $router = $this->getMock("\OC_Router", array('create'));
+
+ // we expect create to be called once:
+ $router
+ ->expects($this->once())
+ ->method('create')
+ ->with($this->equalTo('app1.' . $name), $this->equalTo($url))
+ ->will($this->returnValue($route));
+
+ // load route configuration
+ $container = new DIContainer('app1');
+ $config = new RouteConfig($container, $router, $routes);
+
+ $config->register();
+ }
+
+ private function assertResource($yaml, $resourceName, $url, $controllerName, $paramName)
+ {
+ // router mock
+ $router = $this->getMock("\OC_Router", array('create'));
+
+ // route mocks
+ $indexRoute = $this->mockRoute('GET', $controllerName, 'index');
+ $showRoute = $this->mockRoute('GET', $controllerName, 'show');
+ $createRoute = $this->mockRoute('POST', $controllerName, 'create');
+ $updateRoute = $this->mockRoute('PUT', $controllerName, 'update');
+ $destroyRoute = $this->mockRoute('DELETE', $controllerName, 'destroy');
+
+ $urlWithParam = $url . '/{' . $paramName . '}';
+
+ // we expect create to be called once:
+ $router
+ ->expects($this->at(0))
+ ->method('create')
+ ->with($this->equalTo('app1.' . $resourceName . '.index'), $this->equalTo($url))
+ ->will($this->returnValue($indexRoute));
+
+ $router
+ ->expects($this->at(1))
+ ->method('create')
+ ->with($this->equalTo('app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam))
+ ->will($this->returnValue($showRoute));
+
+ $router
+ ->expects($this->at(2))
+ ->method('create')
+ ->with($this->equalTo('app1.' . $resourceName . '.create'), $this->equalTo($url))
+ ->will($this->returnValue($createRoute));
+
+ $router
+ ->expects($this->at(3))
+ ->method('create')
+ ->with($this->equalTo('app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam))
+ ->will($this->returnValue($updateRoute));
+
+ $router
+ ->expects($this->at(4))
+ ->method('create')
+ ->with($this->equalTo('app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam))
+ ->will($this->returnValue($destroyRoute));
+
+ // load route configuration
+ $container = new DIContainer('app1');
+ $config = new RouteConfig($container, $router, $yaml);
+
+ $config->register();
+ }
+
+ /**
+ * @param $verb
+ * @param $controllerName
+ * @param $actionName
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ private function mockRoute($verb, $controllerName, $actionName)
+ {
+ $container = new DIContainer('app1');
+ $route = $this->getMock("\OC_Route", array('method', 'action'), array(), '', false);
+ $route
+ ->expects($this->exactly(1))
+ ->method('method')
+ ->with($this->equalTo($verb))
+ ->will($this->returnValue($route));
+
+ $route
+ ->expects($this->exactly(1))
+ ->method('action')
+ ->with($this->equalTo(new RouteActionHandler($container, $controllerName, $actionName)))
+ ->will($this->returnValue($route));
+ return $route;
+ }
+
+}
+
+/*
+#
+# sample routes.yaml for ownCloud
+#
+# the section simple describes one route
+
+routes:
+ - name: folders#open
+ url: /folders/{folderId}/open
+ verb: GET
+ # controller: name.split()[0]
+ # action: name.split()[1]
+
+# for a resource following actions will be generated:
+# - index
+# - create
+# - show
+# - update
+# - destroy
+# - new
+resources:
+ accounts:
+ url: /accounts
+
+ folders:
+ url: /accounts/{accountId}/folders
+ # actions can be used to define additional actions on the resource
+ actions:
+ - name: validate
+ verb: GET
+ on-collection: false
+
+ * */
diff --git a/tests/lib/appframework/utility/MethodAnnotationReaderTest.php b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php
new file mode 100644
index 00000000000..bcdcf3de37b
--- /dev/null
+++ b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * ownCloud - App Framework
+ *
+ * @author Bernhard Posselt
+ * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+namespace OC\AppFramework\Utility;
+
+
+require_once __DIR__ . "/../classloader.php";
+
+
+class MethodAnnotationReaderTest extends \PHPUnit_Framework_TestCase {
+
+
+ /**
+ * @Annotation
+ */
+ public function testReadAnnotation(){
+ $reader = new MethodAnnotationReader('\OC\AppFramework\Utility\MethodAnnotationReaderTest',
+ 'testReadAnnotation');
+
+ $this->assertTrue($reader->hasAnnotation('Annotation'));
+ }
+
+
+ /**
+ * @Annotation
+ * @param test
+ */
+ public function testReadAnnotationNoLowercase(){
+ $reader = new MethodAnnotationReader('\OC\AppFramework\Utility\MethodAnnotationReaderTest',
+ 'testReadAnnotationNoLowercase');
+
+ $this->assertTrue($reader->hasAnnotation('Annotation'));
+ $this->assertFalse($reader->hasAnnotation('param'));
+ }
+
+
+}