diff --git a/.gitignore b/.gitignore
index 531e372e6074013d352aabf8db545c5501672dd5..3fb848dbb44f6ae718eda6d1f9851750fcbab965 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
 !/apps/dav
 !/apps/files
 !/apps/files_encryption
+!/apps/federation
 !/apps/encryption
 !/apps/encryption_dummy
 !/apps/files_external
diff --git a/apps/federation/api/ocsauthapi.php b/apps/federation/api/ocsauthapi.php
new file mode 100644
index 0000000000000000000000000000000000000000..42d7113820d7fdd4b57ccfd49f291fe063156dbd
--- /dev/null
+++ b/apps/federation/api/ocsauthapi.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\API;
+
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Http;
+use OCP\BackgroundJob\IJobList;
+use OCP\IRequest;
+use OCP\Security\ISecureRandom;
+use OCP\Security\StringUtils;
+
+/**
+ * Class OCSAuthAPI
+ *
+ * OCS API end-points to exchange shared secret between two connected ownClouds
+ *
+ * @package OCA\Federation\API
+ */
+class OCSAuthAPI {
+
+	/** @var IRequest */
+	private $request;
+
+	/** @var ISecureRandom  */
+	private $secureRandom;
+
+	/** @var IJobList */
+	private $jobList;
+
+	/** @var TrustedServers */
+	private $trustedServers;
+
+	/** @var DbHandler */
+	private $dbHandler;
+
+	/**
+	 * OCSAuthAPI constructor.
+	 *
+	 * @param IRequest $request
+	 * @param ISecureRandom $secureRandom
+	 * @param IJobList $jobList
+	 * @param TrustedServers $trustedServers
+	 * @param DbHandler $dbHandler
+	 */
+	public function __construct(
+		IRequest $request,
+		ISecureRandom $secureRandom,
+		IJobList $jobList,
+		TrustedServers $trustedServers,
+		DbHandler $dbHandler
+	) {
+		$this->request = $request;
+		$this->secureRandom = $secureRandom;
+		$this->jobList = $jobList;
+		$this->trustedServers = $trustedServers;
+		$this->dbHandler = $dbHandler;
+	}
+
+	/**
+	 * request received to ask remote server for a shared secret
+	 *
+	 * @return \OC_OCS_Result
+	 */
+	public function requestSharedSecret() {
+
+		$url = $this->request->getParam('url');
+		$token = $this->request->getParam('token');
+
+		if ($this->trustedServers->isTrustedServer($url) === false) {
+			return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
+		}
+
+		// if both server initiated the exchange of the shared secret the greater
+		// token wins
+		$localToken = $this->dbHandler->getToken($url);
+		if (strcmp($localToken, $token) > 0) {
+			return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
+		}
+
+		$this->jobList->add(
+			'OCA\Federation\BackgroundJob\GetSharedSecret',
+			[
+				'url' => $url,
+				'token' => $token,
+			]
+		);
+
+		return new \OC_OCS_Result(null, Http::STATUS_OK);
+
+	}
+
+	/**
+	 * create shared secret and return it
+	 *
+	 * @return \OC_OCS_Result
+	 */
+	public function getSharedSecret() {
+
+		$url = $this->request->getParam('url');
+		$token = $this->request->getParam('token');
+
+		if (
+			$this->trustedServers->isTrustedServer($url) === false
+			|| $this->isValidToken($url, $token) === false
+		) {
+			return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
+		}
+
+		$sharedSecret = $this->secureRandom->getMediumStrengthGenerator()->generate(32);
+
+		$this->trustedServers->addSharedSecret($url, $sharedSecret);
+		// reset token after the exchange of the shared secret was successful
+		$this->dbHandler->addToken($url, '');
+
+		return new \OC_OCS_Result(['sharedSecret' => $sharedSecret], Http::STATUS_OK);
+
+	}
+
+	protected function isValidToken($url, $token) {
+		$storedToken = $this->dbHandler->getToken($url);
+		return StringUtils::equals($storedToken, $token);
+	}
+
+}
diff --git a/apps/federation/appinfo/app.php b/apps/federation/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..9ed00f23866330f02474e64395426f86c74c33fb
--- /dev/null
+++ b/apps/federation/appinfo/app.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Federation\AppInfo;
+
+$app = new Application();
+$app->registerSettings();
diff --git a/apps/federation/appinfo/application.php b/apps/federation/appinfo/application.php
new file mode 100644
index 0000000000000000000000000000000000000000..350b140b4dd5c8021857e000b0f71363d0a72b4b
--- /dev/null
+++ b/apps/federation/appinfo/application.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Federation\AppInfo;
+
+use OCA\Federation\API\OCSAuthAPI;
+use OCA\Federation\Controller\AuthController;
+use OCA\Federation\Controller\SettingsController;
+use OCA\Federation\DbHandler;
+use OCA\Federation\Middleware\AddServerMiddleware;
+use OCA\Federation\TrustedServers;
+use OCP\API;
+use OCP\App;
+use OCP\AppFramework\IAppContainer;
+
+class Application extends \OCP\AppFramework\App {
+
+	/**
+	 * @param array $urlParams
+	 */
+	public function __construct($urlParams = array()) {
+		parent::__construct('federation', $urlParams);
+		$this->registerService();
+		$this->registerMiddleware();
+	}
+
+	/**
+	 * register setting scripts
+	 */
+	public function registerSettings() {
+		App::registerAdmin('federation', 'settings/settings-admin');
+	}
+
+	private function registerService() {
+		$container = $this->getContainer();
+
+		$container->registerService('addServerMiddleware', function(IAppContainer $c) {
+			return new AddServerMiddleware(
+				$c->getAppName(),
+				\OC::$server->getL10N($c->getAppName()),
+				\OC::$server->getLogger()
+			);
+		});
+
+		$container->registerService('DbHandler', function(IAppContainer $c) {
+			return new DbHandler(
+				\OC::$server->getDatabaseConnection(),
+				\OC::$server->getL10N($c->getAppName())
+			);
+		});
+
+		$container->registerService('TrustedServers', function(IAppContainer $c) {
+			return new TrustedServers(
+				$c->query('DbHandler'),
+				\OC::$server->getHTTPClientService(),
+				\OC::$server->getLogger(),
+				\OC::$server->getJobList(),
+				\OC::$server->getSecureRandom(),
+				\OC::$server->getConfig()
+			);
+		});
+
+		$container->registerService('SettingsController', function (IAppContainer $c) {
+			$server = $c->getServer();
+			return new SettingsController(
+				$c->getAppName(),
+				$server->getRequest(),
+				$server->getL10N($c->getAppName()),
+				$c->query('TrustedServers')
+			);
+		});
+	}
+
+	private function registerMiddleware() {
+		$container = $this->getContainer();
+		$container->registerMiddleware('addServerMiddleware');
+	}
+
+	/**
+	 * register OCS API Calls
+	 */
+	public function registerOCSApi() {
+
+		$container = $this->getContainer();
+		$server = $container->getServer();
+
+		$auth = new OCSAuthAPI(
+			$server->getRequest(),
+			$server->getSecureRandom(),
+			$server->getJobList(),
+			$container->query('TrustedServers'),
+			$container->query('DbHandler')
+
+		);
+
+		API::register('get',
+			'/apps/federation/api/v1/shared-secret',
+			array($auth, 'getSharedSecret'),
+			'federation',
+			API::GUEST_AUTH
+		);
+
+		API::register('post',
+			'/apps/federation/api/v1/request-shared-secret',
+			array($auth, 'requestSharedSecret'),
+			'federation',
+			API::GUEST_AUTH
+		);
+
+	}
+
+}
diff --git a/apps/federation/appinfo/database.xml b/apps/federation/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e0bb241918e26c7f16c0335645256d395d75e5cb
--- /dev/null
+++ b/apps/federation/appinfo/database.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+	<name>*dbname*</name>
+	<create>true</create>
+	<overwrite>false</overwrite>
+	<charset>utf8</charset>
+	<table>
+		<name>*dbprefix*trusted_servers</name>
+		<declaration>
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
+			</field>
+			<field>
+				<name>url</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>512</length>
+				<comments>Url of trusted server</comments>
+			</field>
+			<field>
+				<name>url_hash</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>32</length>
+				<comments>md5 hash of the url without the protocol</comments>
+			</field>
+			<field>
+				<name>token</name>
+				<type>text</type>
+				<length>128</length>
+				<comments>toke used to exchange the shared secret</comments>
+			</field>
+			<field>
+				<name>shared_secret</name>
+				<type>text</type>
+				<length>256</length>
+				<comments>shared secret used to authenticate</comments>
+			</field>
+			<field>
+				<name>status</name>
+				<type>integer</type>
+				<length>4</length>
+				<notnull>true</notnull>
+				<default>2</default>
+				<comments>current status of the connection</comments>
+			</field>
+			<index>
+				<name>url_hash</name>
+				<unique>true</unique>
+				<field>
+					<name>url_hash</name>
+					<sorting>ascending</sorting>
+				</field>
+			</index>
+		</declaration>
+	</table>
+</database>
diff --git a/apps/federation/appinfo/info.xml b/apps/federation/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..53b2926ba539886299b42f193d5964d63cd9f04b
--- /dev/null
+++ b/apps/federation/appinfo/info.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<info>
+    <id>federation</id>
+    <name>Federation</name>
+    <description>ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing.</description>
+    <licence>AGPL</licence>
+    <author>Bjoern Schiessle</author>
+    <version>0.0.1</version>
+    <namespace>Federation</namespace>
+    <category>other</category>
+    <dependencies>
+        <owncloud min-version="9.0" />
+    </dependencies>
+</info>
diff --git a/apps/federation/appinfo/routes.php b/apps/federation/appinfo/routes.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c1629a4fc6ef3368b2420ae1962421ec3c91498
--- /dev/null
+++ b/apps/federation/appinfo/routes.php
@@ -0,0 +1,47 @@
+<?php
+/**
+* @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+*
+ * This code is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+$application = new \OCA\Federation\AppInfo\Application();
+
+$application->registerRoutes(
+	$this,
+	[
+		'routes' => [
+			[
+				'name' => 'Settings#addServer',
+				'url' => '/trusted-servers',
+				'verb' => 'POST'
+			],
+			[
+				'name' => 'Settings#removeServer',
+				'url' => '/trusted-servers/{id}',
+				'verb' => 'DELETE'
+			],
+			[
+				'name' => 'Settings#autoAddServers',
+				'url' => '/auto-add-servers',
+				'verb' => 'POST'
+			],
+		]
+	]
+);
+
+$application->registerOCSApi();
diff --git a/apps/federation/backgroundjob/getsharedsecret.php b/apps/federation/backgroundjob/getsharedsecret.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb55fa2d6ab183d02f8a48ba657802331bd8c85c
--- /dev/null
+++ b/apps/federation/backgroundjob/getsharedsecret.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\BackgroundJob;
+
+use GuzzleHttp\Exception\ClientException;
+use OC\BackgroundJob\JobList;
+use OC\BackgroundJob\QueuedJob;
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Http;
+use OCP\BackgroundJob\IJobList;
+use OCP\Http\Client\IClient;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+
+/**
+ * Class GetSharedSecret
+ *
+ * request shared secret from remote ownCloud
+ *
+ * @package OCA\Federation\Backgroundjob
+ */
+class GetSharedSecret extends QueuedJob{
+
+	/** @var IClient */
+	private $httpClient;
+
+	/** @var IJobList */
+	private $jobList;
+
+	/** @var IURLGenerator */
+	private $urlGenerator;
+
+	/** @var TrustedServers  */
+	private $trustedServers;
+
+	/** @var DbHandler */
+	private $dbHandler;
+
+	/** @var ILogger */
+	private $logger;
+
+	private $endPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret?format=json';
+
+	/**
+	 * RequestSharedSecret constructor.
+	 *
+	 * @param IClient $httpClient
+	 * @param IURLGenerator $urlGenerator
+	 * @param IJobList $jobList
+	 * @param TrustedServers $trustedServers
+	 * @param ILogger $logger
+	 * @param DbHandler $dbHandler
+	 */
+	public function __construct(
+		IClient $httpClient = null,
+		IURLGenerator $urlGenerator = null,
+		IJobList $jobList = null,
+		TrustedServers $trustedServers = null,
+		ILogger $logger = null,
+		dbHandler $dbHandler = null
+	) {
+		$this->logger = $logger ? $logger : \OC::$server->getLogger();
+		$this->httpClient = $httpClient ? $httpClient : \OC::$server->getHTTPClientService()->newClient();
+		$this->jobList = $jobList ? $jobList : \OC::$server->getJobList();
+		$this->urlGenerator = $urlGenerator ? $urlGenerator : \OC::$server->getURLGenerator();
+		$this->dbHandler = $dbHandler ? $dbHandler : new DbHandler(\OC::$server->getDatabaseConnection(), \OC::$server->getL10N('federation'));
+		if ($trustedServers) {
+			$this->trustedServers = $trustedServers;
+		} else {
+			$this->trustedServers = new TrustedServers(
+					$this->dbHandler,
+					\OC::$server->getHTTPClientService(),
+					\OC::$server->getLogger(),
+					$this->jobList,
+					\OC::$server->getSecureRandom(),
+					\OC::$server->getConfig()
+			);
+		}
+	}
+
+	/**
+	 * run the job, then remove it from the joblist
+	 *
+	 * @param JobList $jobList
+	 * @param ILogger $logger
+	 */
+	public function execute($jobList, ILogger $logger = null) {
+		$jobList->remove($this, $this->argument);
+		$target = $this->argument['url'];
+		// only execute if target is still in the list of trusted domains
+		if ($this->trustedServers->isTrustedServer($target)) {
+			$this->parentExecute($jobList, $logger);
+		}
+	}
+
+	/**
+	 * call execute() method of parent
+	 *
+	 * @param JobList $jobList
+	 * @param ILogger $logger
+	 */
+	protected function parentExecute($jobList, $logger) {
+		parent::execute($jobList, $logger);
+	}
+
+	protected function run($argument) {
+		$target = $argument['url'];
+		$source = $this->urlGenerator->getAbsoluteURL('/');
+		$source = rtrim($source, '/');
+		$token = $argument['token'];
+
+		try {
+			$result = $this->httpClient->get(
+				$target . $this->endPoint,
+				[
+					'query' =>
+						[
+							'url' => $source,
+							'token' => $token
+						],
+					'timeout' => 3,
+					'connect_timeout' => 3,
+				]
+			);
+
+			$status = $result->getStatusCode();
+
+		} catch (ClientException $e) {
+			$status = $e->getCode();
+		}
+
+		// if we received a unexpected response we try again later
+		if (
+			$status !== Http::STATUS_OK
+			&& $status !== Http::STATUS_FORBIDDEN
+		) {
+			$this->jobList->add(
+				'OCA\Federation\BackgroundJob\GetSharedSecret',
+				$argument
+			);
+		}  else {
+			// reset token if we received a valid response
+			$this->dbHandler->addToken($target, '');
+		}
+
+		if ($status === Http::STATUS_OK) {
+			$body = $result->getBody();
+			$result = json_decode($body, true);
+			if (isset($result['ocs']['data']['sharedSecret'])) {
+				$this->trustedServers->addSharedSecret(
+						$target,
+						$result['ocs']['data']['sharedSecret']
+				);
+			} else {
+				$this->logger->error(
+						'remote server "' . $target . '"" does not return a valid shared secret',
+						['app' => 'federation']
+				);
+				$this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE);
+			}
+		}
+
+	}
+}
diff --git a/apps/federation/backgroundjob/requestsharedsecret.php b/apps/federation/backgroundjob/requestsharedsecret.php
new file mode 100644
index 0000000000000000000000000000000000000000..24d8adada1113821e91cfb729b0839ebd2670847
--- /dev/null
+++ b/apps/federation/backgroundjob/requestsharedsecret.php
@@ -0,0 +1,164 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\BackgroundJob;
+
+
+use GuzzleHttp\Exception\ClientException;
+use OC\BackgroundJob\JobList;
+use OC\BackgroundJob\QueuedJob;
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Http;
+use OCP\BackgroundJob\IJobList;
+use OCP\Http\Client\IClient;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+
+/**
+ * Class RequestSharedSecret
+ *
+ * Ask remote ownCloud to request a sharedSecret from this server
+ *
+ * @package OCA\Federation\Backgroundjob
+ */
+class RequestSharedSecret extends QueuedJob {
+
+	/** @var IClient */
+	private $httpClient;
+
+	/** @var IJobList */
+	private $jobList;
+
+	/** @var IURLGenerator */
+	private $urlGenerator;
+
+	/** @var DbHandler */
+	private $dbHandler;
+
+	/** @var TrustedServers */
+	private $trustedServers;
+
+	private $endPoint = '/ocs/v2.php/apps/federation/api/v1/request-shared-secret?format=json';
+
+	/**
+	 * RequestSharedSecret constructor.
+	 *
+	 * @param IClient $httpClient
+	 * @param IURLGenerator $urlGenerator
+	 * @param IJobList $jobList
+	 * @param TrustedServers $trustedServers
+	 * @param DbHandler $dbHandler
+	 */
+	public function __construct(
+		IClient $httpClient = null,
+		IURLGenerator $urlGenerator = null,
+		IJobList $jobList = null,
+		TrustedServers $trustedServers = null,
+		dbHandler $dbHandler = null
+	) {
+		$this->httpClient = $httpClient ? $httpClient : \OC::$server->getHTTPClientService()->newClient();
+		$this->jobList = $jobList ? $jobList : \OC::$server->getJobList();
+		$this->urlGenerator = $urlGenerator ? $urlGenerator : \OC::$server->getURLGenerator();
+		$this->dbHandler = $dbHandler ? $dbHandler : new DbHandler(\OC::$server->getDatabaseConnection(), \OC::$server->getL10N('federation'));
+		if ($trustedServers) {
+			$this->trustedServers = $trustedServers;
+		} else {
+			$this->trustedServers = new TrustedServers(
+				$this->dbHandler,
+				\OC::$server->getHTTPClientService(),
+				\OC::$server->getLogger(),
+				$this->jobList,
+				\OC::$server->getSecureRandom(),
+				\OC::$server->getConfig()
+			);
+		}
+	}
+
+
+	/**
+	 * run the job, then remove it from the joblist
+	 *
+	 * @param JobList $jobList
+	 * @param ILogger $logger
+	 */
+	public function execute($jobList, ILogger $logger = null) {
+		$jobList->remove($this, $this->argument);
+		$target = $this->argument['url'];
+		// only execute if target is still in the list of trusted domains
+		if ($this->trustedServers->isTrustedServer($target)) {
+			$this->parentExecute($jobList, $logger);
+		}
+	}
+
+	/**
+	 * @param JobList $jobList
+	 * @param ILogger $logger
+	 */
+	protected function parentExecute($jobList, $logger) {
+		parent::execute($jobList, $logger);
+	}
+
+	protected function run($argument) {
+
+		$target = $argument['url'];
+		$source = $this->urlGenerator->getAbsoluteURL('/');
+		$source = rtrim($source, '/');
+		$token = $argument['token'];
+
+		try {
+			$result = $this->httpClient->post(
+				$target . $this->endPoint,
+				[
+					'body' => [
+						'url' => $source,
+						'token' => $token,
+					],
+					'timeout' => 3,
+					'connect_timeout' => 3,
+				]
+			);
+
+			$status = $result->getStatusCode();
+
+		} catch (ClientException $e) {
+			$status = $e->getCode();
+		}
+
+		// if we received a unexpected response we try again later
+		if (
+			$status !== Http::STATUS_OK
+			&& $status !== Http::STATUS_FORBIDDEN
+		) {
+			$this->jobList->add(
+				'OCA\Federation\BackgroundJob\RequestSharedSecret',
+				$argument
+			);
+		}
+
+		if ($status === Http::STATUS_FORBIDDEN) {
+			// clear token if remote server refuses to ask for shared secret
+			$this->dbHandler->addToken($target, '');
+		}
+
+	}
+}
diff --git a/apps/federation/controller/settingscontroller.php b/apps/federation/controller/settingscontroller.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e28cd60cfa31c7a5d3c57a1e9a4955f00b5df6a
--- /dev/null
+++ b/apps/federation/controller/settingscontroller.php
@@ -0,0 +1,123 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Federation\Controller;
+
+use OC\HintException;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\IRequest;
+
+
+class SettingsController extends Controller {
+
+	/** @var IL10N */
+	private $l;
+
+	/** @var  TrustedServers */
+	private $trustedServers;
+
+	/**
+	 * @param string $AppName
+	 * @param IRequest $request
+	 * @param IL10N $l10n
+	 * @param TrustedServers $trustedServers
+	 */
+	public function __construct($AppName,
+								IRequest $request,
+								IL10N $l10n,
+								TrustedServers $trustedServers
+	) {
+		parent::__construct($AppName, $request);
+		$this->l = $l10n;
+		$this->trustedServers = $trustedServers;
+	}
+
+
+	/**
+	 * add server to the list of trusted ownClouds
+	 *
+	 * @param string $url
+	 * @return DataResponse
+	 * @throws HintException
+	 */
+	public function addServer($url) {
+		$this->checkServer($url);
+		$id = $this->trustedServers->addServer($url);
+
+		return new DataResponse(
+			[
+				'url' => $url,
+				'id' => $id,
+				'message' => (string) $this->l->t('Server added to the list of trusted ownClouds')
+			]
+		);
+	}
+
+	/**
+	 * add server to the list of trusted ownClouds
+	 *
+	 * @param int $id
+	 * @return DataResponse
+	 */
+	public function removeServer($id) {
+		$this->trustedServers->removeServer($id);
+		return new DataResponse();
+	}
+
+	/**
+	 * enable/disable to automatically add servers to the list of trusted servers
+	 * once a federated share was created and accepted successfully
+	 *
+	 * @param bool $autoAddServers
+	 */
+	public function autoAddServers($autoAddServers) {
+		$this->trustedServers->setAutoAddServers($autoAddServers);
+	}
+
+	/**
+	 * check if the server should be added to the list of trusted servers or not
+	 *
+	 * @param string $url
+	 * @return bool
+	 * @throws HintException
+	 */
+	protected function checkServer($url) {
+		if ($this->trustedServers->isTrustedServer($url) === true) {
+			$message = 'Server is already in the list of trusted servers.';
+			$hint = $this->l->t('Server is already in the list of trusted servers.');
+			throw new HintException($message, $hint);
+		}
+
+		if ($this->trustedServers->isOwnCloudServer($url) === false) {
+			$message = 'No ownCloud server found';
+			$hint = $this->l->t('No ownCloud server found');
+			throw new HintException($message, $hint);
+		}
+
+		return true;
+	}
+
+}
diff --git a/apps/federation/css/settings-admin.css b/apps/federation/css/settings-admin.css
new file mode 100644
index 0000000000000000000000000000000000000000..55b1dd64d15d7eb0595da755687e294991652491
--- /dev/null
+++ b/apps/federation/css/settings-admin.css
@@ -0,0 +1,26 @@
+#ocFederationSettings p {
+	padding-top: 10px;
+}
+
+#listOfTrustedServers li {
+	padding-top: 10px;
+	padding-left: 20px;
+}
+
+.removeTrustedServer {
+	display: none;
+	vertical-align:middle;
+	padding-left: 10px;
+}
+
+#ocFederationAddServerButton {
+	cursor: pointer;
+}
+
+#listOfTrustedServers li:hover {
+	cursor: pointer;
+}
+
+#listOfTrustedServers .status {
+	margin-right: 10px;
+}
diff --git a/apps/federation/js/settings-admin.js b/apps/federation/js/settings-admin.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d531b39d8c4cd4c50450570c64d559118504d26
--- /dev/null
+++ b/apps/federation/js/settings-admin.js
@@ -0,0 +1,82 @@
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+$(document).ready(function () {
+
+	// show input field to add a new trusted server
+	$("#ocFederationAddServer").on('click', function() {
+		$('#ocFederationAddServerButton').addClass('hidden');
+		$("#serverUrl").removeClass('hidden');
+		$("#serverUrl").focus();
+	});
+
+	// add new trusted server
+	$("#serverUrl").keyup(function (e) {
+		if (e.keyCode === 13) { // add server on "enter"
+			var url = $('#serverUrl').val();
+			OC.msg.startSaving('#ocFederationAddServer .msg');
+			$.post(
+				OC.generateUrl('/apps/federation/trusted-servers'),
+				{
+					url: url
+				}
+			).done(function (data) {
+					$('#serverUrl').attr('value', '');
+					$('ul#listOfTrustedServers').prepend(
+						$('<li>')
+								.attr('id', data.id)
+								.attr('class', 'icon-delete')
+								.html('<span class="status indeterminate"></span>' + data.url)
+					);
+					OC.msg.finishedSuccess('#ocFederationAddServer .msg', data.message);
+				})
+				.fail(function (jqXHR) {
+					OC.msg.finishedError('#ocFederationAddServer .msg', JSON.parse(jqXHR.responseText).message);
+				});
+		} else if (e.keyCode === 27) { // hide input filed again in ESC
+			$('#ocFederationAddServerButton').toggleClass('hidden');
+			$("#serverUrl").toggleClass('hidden');
+		}
+	});
+
+	// remove trusted server from list
+	$( "#listOfTrustedServers" ).on('click', 'li', function() {
+		var id = $(this).attr('id');
+		var $this = $(this);
+		$.ajax({
+			url: OC.generateUrl('/apps/federation/trusted-servers/' + id),
+			type: 'DELETE',
+			success: function(response) {
+				$this.remove();
+			}
+		});
+
+	});
+
+	$("#ocFederationSettings #autoAddServers").change(function() {
+		$.post(
+				OC.generateUrl('/apps/federation/auto-add-servers'),
+				{
+					autoAddServers: $(this).is(":checked")
+				}
+		);
+	});
+
+});
diff --git a/apps/federation/lib/dbhandler.php b/apps/federation/lib/dbhandler.php
new file mode 100644
index 0000000000000000000000000000000000000000..61ba5c87cfd9013336a8d3bc539d8809dff3efd2
--- /dev/null
+++ b/apps/federation/lib/dbhandler.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation;
+
+
+use OC\Files\Filesystem;
+use OC\HintException;
+use OCP\IDBConnection;
+use OCP\IL10N;
+
+/**
+ * Class DbHandler
+ *
+ * handles all database calls for the federation app
+ *
+ * @group DB
+ * @package OCA\Federation
+ */
+class DbHandler {
+
+	/** @var  IDBConnection */
+	private $connection;
+
+	/** @var  IL10N */
+	private $l;
+
+	/** @var string  */
+	private $dbTable = 'trusted_servers';
+
+	/**
+	 * @param IDBConnection $connection
+	 * @param IL10N $il10n
+	 */
+	public function __construct(
+		IDBConnection $connection,
+		IL10N $il10n
+	) {
+		$this->connection = $connection;
+		$this->IL10N = $il10n;
+	}
+
+	/**
+	 * add server to the list of trusted ownCloud servers
+	 *
+	 * @param string $url
+	 * @return int
+	 * @throws HintException
+	 */
+	public function addServer($url) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->insert($this->dbTable)
+			->values(
+				[
+					'url' =>  $query->createParameter('url'),
+					'url_hash' => $query->createParameter('url_hash'),
+				]
+			)
+			->setParameter('url', $url)
+			->setParameter('url_hash', $hash);
+
+		$result = $query->execute();
+
+		if ($result) {
+			return (int)$this->connection->lastInsertId('*PREFIX*'.$this->dbTable);
+		} else {
+			$message = 'Internal failure, Could not add ownCloud as trusted server: ' . $url;
+			$message_t = $this->l->t('Could not add server');
+			throw new HintException($message, $message_t);
+		}
+	}
+
+	/**
+	 * remove server from the list of trusted ownCloud servers
+	 *
+	 * @param int $id
+	 */
+	public function removeServer($id) {
+		$query = $this->connection->getQueryBuilder();
+		$query->delete($this->dbTable)
+			->where($query->expr()->eq('id', $query->createParameter('id')))
+			->setParameter('id', $id);
+		$query->execute();
+	}
+
+	/**
+	 * get all trusted servers
+	 *
+	 * @return array
+	 */
+	public function getAllServer() {
+		$query = $this->connection->getQueryBuilder();
+		$query->select(['url', 'id', 'status'])->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		return $result;
+	}
+
+	/**
+	 * check if server already exists in the database table
+	 *
+	 * @param string $url
+	 * @return bool
+	 */
+	public function serverExists($url) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->select('url')->from($this->dbTable)
+			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+			->setParameter('url_hash', $hash);
+		$result = $query->execute()->fetchAll();
+
+		return !empty($result);
+	}
+
+	/**
+	 * write token to database. Token is used to exchange the secret
+	 *
+	 * @param string $url
+	 * @param string $token
+	 */
+	public function addToken($url, $token) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->update($this->dbTable)
+			->set('token', $query->createParameter('token'))
+			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+			->setParameter('url_hash', $hash)
+			->setParameter('token', $token);
+		$query->execute();
+	}
+
+	/**
+	 * get token stored in database
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	public function getToken($url) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->select('token')->from($this->dbTable)
+			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+			->setParameter('url_hash', $hash);
+
+		$result = $query->execute()->fetch();
+		return $result['token'];
+	}
+
+	/**
+	 * add shared Secret to database
+	 *
+	 * @param string $url
+	 * @param string $sharedSecret
+	 */
+	public function addSharedSecret($url, $sharedSecret) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->update($this->dbTable)
+			->set('shared_secret', $query->createParameter('sharedSecret'))
+			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+			->setParameter('url_hash', $hash)
+			->setParameter('sharedSecret', $sharedSecret);
+		$query->execute();
+	}
+
+	/**
+	 * get shared secret from database
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	public function getSharedSecret($url) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->select('shared_secret')->from($this->dbTable)
+			->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+			->setParameter('url_hash', $hash);
+
+		$result = $query->execute()->fetch();
+		return $result['shared_secret'];
+	}
+
+	/**
+	 * set server status
+	 *
+	 * @param string $url
+	 * @param int $status
+	 */
+	public function setServerStatus($url, $status) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->update($this->dbTable)
+				->set('status', $query->createParameter('status'))
+				->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+				->setParameter('url_hash', $hash)
+				->setParameter('status', $status);
+		$query->execute();
+	}
+
+	/**
+	 * get server status
+	 *
+	 * @param string $url
+	 * @return int
+	 */
+	public function getServerStatus($url) {
+		$hash = $this->hash($url);
+		$query = $this->connection->getQueryBuilder();
+		$query->select('status')->from($this->dbTable)
+				->where($query->expr()->eq('url_hash', $query->createParameter('url_hash')))
+				->setParameter('url_hash', $hash);
+
+		$result = $query->execute()->fetch();
+		return (int)$result['status'];
+	}
+
+	/**
+	 * create hash from URL
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	protected function hash($url) {
+		$normalized = $this->normalizeUrl($url);
+		return md5($normalized);
+	}
+
+	/**
+	 * normalize URL, used to create the md5 hash
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	protected function normalizeUrl($url) {
+		$normalized = $url;
+
+		if (strpos($url, 'https://') === 0) {
+			$normalized = substr($url, strlen('https://'));
+		} else if (strpos($url, 'http://') === 0) {
+			$normalized = substr($url, strlen('http://'));
+		}
+
+		$normalized = Filesystem::normalizePath($normalized);
+		$normalized = trim($normalized, '/');
+
+		return $normalized;
+	}
+
+}
diff --git a/apps/federation/lib/trustedservers.php b/apps/federation/lib/trustedservers.php
new file mode 100644
index 0000000000000000000000000000000000000000..96a291780760e60975e130ea02ff6fe419573b73
--- /dev/null
+++ b/apps/federation/lib/trustedservers.php
@@ -0,0 +1,254 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation;
+
+use OCP\AppFramework\Http;
+use OCP\BackgroundJob\IJobList;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\Security\ISecureRandom;
+
+class TrustedServers {
+
+	/** after a user list was exchanged at least once successfully */
+	const STATUS_OK = 1;
+	/** waiting for shared secret or initial user list exchange */
+	const STATUS_PENDING = 2;
+	/** something went wrong, misconfigured server, software bug,... user interaction needed */
+	const STATUS_FAILURE = 3;
+
+	/** @var  dbHandler */
+	private $dbHandler;
+
+	/** @var  IClientService */
+	private $httpClientService;
+
+	/** @var ILogger */
+	private $logger;
+
+	/** @var IJobList */
+	private $jobList;
+
+	/** @var ISecureRandom */
+	private $secureRandom;
+
+	/** @var IConfig */
+	private $config;
+
+	/**
+	 * @param DbHandler $dbHandler
+	 * @param IClientService $httpClientService
+	 * @param ILogger $logger
+	 * @param IJobList $jobList
+	 * @param ISecureRandom $secureRandom
+	 * @param IConfig $config
+	 */
+	public function __construct(
+		DbHandler $dbHandler,
+		IClientService $httpClientService,
+		ILogger $logger,
+		IJobList $jobList,
+		ISecureRandom $secureRandom,
+		IConfig $config
+	) {
+		$this->dbHandler = $dbHandler;
+		$this->httpClientService = $httpClientService;
+		$this->logger = $logger;
+		$this->jobList = $jobList;
+		$this->secureRandom = $secureRandom;
+		$this->config = $config;
+	}
+
+	/**
+	 * add server to the list of trusted ownCloud servers
+	 *
+	 * @param $url
+	 * @return int server id
+	 */
+	public function addServer($url) {
+		$url = $this->updateProtocol($url);
+		$result = $this->dbHandler->addServer($url);
+		if ($result) {
+			$token = $this->secureRandom->getMediumStrengthGenerator()->generate(16);
+			$this->dbHandler->addToken($url, $token);
+			$this->jobList->add(
+				'OCA\Federation\BackgroundJob\RequestSharedSecret',
+				[
+					'url' => $url,
+					'token' => $token
+				]
+			);
+		}
+
+		return $result;
+	}
+
+	/**
+	 * enable/disable to automatically add servers to the list of trusted servers
+	 * once a federated share was created and accepted successfully
+	 *
+	 * @param bool $status
+	 */
+	public function setAutoAddServers($status) {
+		$value = $status ? '1' : '0';
+		$this->config->setAppValue('federation', 'autoAddServers', $value);
+	}
+
+	/**
+	 * return if we automatically add servers to the list of trusted servers
+	 * once a federated share was created and accepted successfully
+	 *
+	 * @return bool
+	 */
+	public function getAutoAddServers() {
+		$value = $this->config->getAppValue('federation', 'autoAddServers', '1');
+		return $value === '1';
+	}
+
+	/**
+	 * get shared secret for the given server
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	public function getSharedSecret($url) {
+		return $this->dbHandler->getSharedSecret($url);
+	}
+
+	/**
+	 * add shared secret for the given server
+	 *
+	 * @param string $url
+	 * @param $sharedSecret
+	 */
+	public function addSharedSecret($url, $sharedSecret) {
+		$this->dbHandler->addSharedSecret($url, $sharedSecret);
+	}
+
+	/**
+	 * remove server from the list of trusted ownCloud servers
+	 *
+	 * @param int $id
+	 */
+	public function removeServer($id) {
+		$this->dbHandler->removeServer($id);
+	}
+
+	/**
+	 * get all trusted servers
+	 *
+	 * @return array
+	 */
+	public function getServers() {
+		return $this->dbHandler->getAllServer();
+	}
+
+	/**
+	 * check if given server is a trusted ownCloud server
+	 *
+	 * @param string $url
+	 * @return bool
+	 */
+	public function isTrustedServer($url) {
+		return $this->dbHandler->serverExists($url);
+	}
+
+	/**
+	 * set server status
+	 *
+	 * @param string $url
+	 * @param int $status
+	 */
+	public function setServerStatus($url, $status) {
+		$this->dbHandler->setServerStatus($url, $status);
+	}
+
+	/**
+	 * @param string $url
+	 * @return int
+	 */
+	public function getServerStatus($url) {
+		return $this->dbHandler->getServerStatus($url);
+	}
+
+	/**
+	 * check if URL point to a ownCloud server
+	 *
+	 * @param string $url
+	 * @return bool
+	 */
+	public function isOwnCloudServer($url) {
+		$isValidOwnCloud = false;
+		$client = $this->httpClientService->newClient();
+		try {
+			$result = $client->get(
+				$url . '/status.php',
+				[
+					'timeout' => 3,
+					'connect_timeout' => 3,
+				]
+			);
+			if ($result->getStatusCode() === Http::STATUS_OK) {
+				$isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody());
+			}
+		} catch (\Exception $e) {
+			$this->logger->error($e->getMessage(), ['app' => 'federation']);
+			return false;
+		}
+		return $isValidOwnCloud;
+	}
+
+	/**
+	 * check if ownCloud version is >= 9.0
+	 *
+	 * @param $statusphp
+	 * @return bool
+	 */
+	protected function checkOwnCloudVersion($statusphp) {
+		$decoded = json_decode($statusphp, true);
+		if (!empty($decoded) && isset($decoded['version'])) {
+			return version_compare($decoded['version'], '9.0.0', '>=');
+		}
+		return false;
+	}
+
+	/**
+	 * check if the URL contain a protocol, if not add https
+	 *
+	 * @param string $url
+	 * @return string
+	 */
+	protected function updateProtocol($url) {
+		if (
+			strpos($url, 'https://') === 0
+			|| strpos($url, 'http://') === 0
+		) {
+
+			return $url;
+
+		}
+
+		return 'https://' . $url;
+	}
+}
diff --git a/apps/federation/middleware/addservermiddleware.php b/apps/federation/middleware/addservermiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..56552021dc268ff5393771181c8305c6b32d2bfb
--- /dev/null
+++ b/apps/federation/middleware/addservermiddleware.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Federation\Middleware ;
+
+use OC\HintException;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Middleware;
+use OCP\IL10N;
+use OCP\ILogger;
+
+class AddServerMiddleware extends Middleware {
+
+	/** @var  string */
+	protected $appName;
+
+	/** @var  IL10N */
+	protected $l;
+
+	/** @var  ILogger */
+	protected $logger;
+
+	public function __construct($appName, IL10N $l, ILogger $logger) {
+		$this->appName = $appName;
+		$this->l = $l;
+		$this->logger = $logger;
+	}
+
+	/**
+	 * Log error message and return a response which can be displayed to the user
+	 *
+	 * @param \OCP\AppFramework\Controller $controller
+	 * @param string $methodName
+	 * @param \Exception $exception
+	 * @return JSONResponse
+	 */
+	public function afterException($controller, $methodName, \Exception $exception) {
+		$this->logger->error($exception->getMessage(), ['app' => $this->appName]);
+		if ($exception instanceof HintException) {
+			$message = $exception->getHint();
+		} else {
+			$message = $this->l->t('Unknown error');
+		}
+
+		return new JSONResponse(
+			['message' => $message],
+			Http::STATUS_BAD_REQUEST
+		);
+
+	}
+
+}
diff --git a/apps/federation/settings/settings-admin.php b/apps/federation/settings/settings-admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..76ae0c3b6e09bce348fba9b86c8a2d2a8e6d47fe
--- /dev/null
+++ b/apps/federation/settings/settings-admin.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+\OC_Util::checkAdminUser();
+
+$template = new OCP\Template('federation', 'settings-admin');
+
+$dbHandler = new \OCA\Federation\DbHandler(
+	\OC::$server->getDatabaseConnection(),
+	\OC::$server->getL10N('federation')
+);
+
+$trustedServers = new \OCA\Federation\TrustedServers(
+	$dbHandler,
+	\OC::$server->getHTTPClientService(),
+	\OC::$server->getLogger(),
+	\OC::$server->getJobList(),
+	\OC::$server->getSecureRandom(),
+	\OC::$server->getConfig()
+);
+
+$template->assign('trustedServers', $trustedServers->getServers());
+$template->assign('autoAddServers', $trustedServers->getAutoAddServers());
+
+return $template->fetchPage();
diff --git a/apps/federation/templates/settings-admin.php b/apps/federation/templates/settings-admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..854bb74417937a43e9aa63e419288e1a45c16a25
--- /dev/null
+++ b/apps/federation/templates/settings-admin.php
@@ -0,0 +1,40 @@
+<?php
+/** @var array $_ */
+use OCA\Federation\TrustedServers;
+
+/** @var OC_L10N $l */
+script('federation', 'settings-admin');
+style('federation', 'settings-admin')
+?>
+<div id="ocFederationSettings" class="section">
+	<h2><?php p($l->t('Federation')); ?></h2>
+	<em><?php p($l->t('ownCloud Federation allows you to connect with other trusted ownClouds to exchange the user directory. For example this will be used to auto-complete external users for federated sharing.')); ?></em>
+
+	<p>
+		<input id="autoAddServers" type="checkbox" class="checkbox" <?php if($_['autoAddServers']) p('checked'); ?> />
+		<label for="autoAddServers">Add server automatically once a federated share was created successfully</label>
+	</p>
+
+	<h3>Trusted ownCloud Servers</h3>
+	<p id="ocFederationAddServer">
+		<button id="ocFederationAddServerButton" class="">+ Add ownCloud server</button>
+		<input id="serverUrl" class="hidden" type="text" value="" placeholder="ownCloud Server" name="server_url"/>
+		<span class="msg"></span>
+	</p>
+	<ul id="listOfTrustedServers">
+		<?php foreach($_['trustedServers'] as $trustedServer) { ?>
+			<li id="<?php p($trustedServer['id']); ?>" class="icon-delete">
+				<?php if((int)$trustedServer['status'] === TrustedServers::STATUS_OK) { ?>
+					<span class="status success"></span>
+				<?php } elseif((int)$trustedServer['status'] === TrustedServers::STATUS_PENDING) { ?>
+					<span class="status indeterminate"></span>
+				<?php } else {?>
+					<span class="status error"></span>
+				<?php } ?>
+				<?php p($trustedServer['url']); ?>
+			</li>
+		<?php } ?>
+	</ul>
+
+</div>
+
diff --git a/apps/federation/tests/api/ocsauthapitest.php b/apps/federation/tests/api/ocsauthapitest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a334686c24e424c0c98796144d3969f7baca1f6e
--- /dev/null
+++ b/apps/federation/tests/api/ocsauthapitest.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\API;
+
+
+use OC\BackgroundJob\JobList;
+use OCA\Federation\API\OCSAuthAPI;
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Http;
+use OCP\IRequest;
+use OCP\Security\ISecureRandom;
+use Test\TestCase;
+
+class OCSAuthAPITest extends TestCase {
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IRequest */
+	private $request;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | ISecureRandom  */
+	private $secureRandom;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | JobList */
+	private $jobList;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers */
+	private $trustedServers;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | DbHandler */
+	private $dbHandler;
+
+	/** @var  OCSAuthApi */
+	private $ocsAuthApi;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->request = $this->getMock('OCP\IRequest');
+		$this->secureRandom = $this->getMock('OCP\Security\ISecureRandom');
+		$this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->disableOriginalConstructor()->getMock();
+		$this->dbHandler = $this->getMockBuilder('OCA\Federation\DbHandler')
+			->disableOriginalConstructor()->getMock();
+		$this->jobList = $this->getMockBuilder('OC\BackgroundJob\JobList')
+			->disableOriginalConstructor()->getMock();
+
+		$this->ocsAuthApi = new OCSAuthAPI(
+			$this->request,
+			$this->secureRandom,
+			$this->jobList,
+			$this->trustedServers,
+			$this->dbHandler
+		);
+
+	}
+
+	/**
+	 * @dataProvider dataTestRequestSharedSecret
+	 *
+	 * @param string $token
+	 * @param string $localToken
+	 * @param bool $isTrustedServer
+	 * @param int $expected
+	 */
+	public function testRequestSharedSecret($token, $localToken, $isTrustedServer, $expected) {
+
+		$url = 'url';
+
+		$this->request->expects($this->at(0))->method('getParam')->with('url')->willReturn($url);
+		$this->request->expects($this->at(1))->method('getParam')->with('token')->willReturn($token);
+		$this->trustedServers
+			->expects($this->once())
+			->method('isTrustedServer')->with($url)->willReturn($isTrustedServer);
+		$this->dbHandler->expects($this->any())
+			->method('getToken')->with($url)->willReturn($localToken);
+
+		if ($expected === Http::STATUS_OK) {
+			$this->jobList->expects($this->once())->method('add')
+				->with('OCA\Federation\BackgroundJob\GetSharedSecret', ['url' => $url, 'token' => $token]);
+		} else {
+			$this->jobList->expects($this->never())->method('add');
+		}
+
+		$result = $this->ocsAuthApi->requestSharedSecret();
+		$this->assertSame($expected, $result->getStatusCode());
+	}
+
+	public function dataTestRequestSharedSecret() {
+		return [
+			['token2', 'token1', true, Http::STATUS_OK],
+			['token1', 'token2', false, Http::STATUS_FORBIDDEN],
+			['token1', 'token2', true, Http::STATUS_FORBIDDEN],
+		];
+	}
+
+	/**
+	 * @dataProvider dataTestGetSharedSecret
+	 *
+	 * @param bool $isTrustedServer
+	 * @param bool $isValidToken
+	 * @param int $expected
+	 */
+	public function testGetSharedSecret($isTrustedServer, $isValidToken, $expected) {
+
+		$url = 'url';
+		$token = 'token';
+
+		$this->request->expects($this->at(0))->method('getParam')->with('url')->willReturn($url);
+		$this->request->expects($this->at(1))->method('getParam')->with('token')->willReturn($token);
+
+		/** @var OCSAuthAPI | \PHPUnit_Framework_MockObject_MockObject $ocsAuthApi */
+		$ocsAuthApi = $this->getMockBuilder('OCA\Federation\API\OCSAuthAPI')
+			->setConstructorArgs(
+				[
+					$this->request,
+					$this->secureRandom,
+					$this->jobList,
+					$this->trustedServers,
+					$this->dbHandler
+				]
+			)->setMethods(['isValidToken'])->getMock();
+
+		$this->trustedServers
+			->expects($this->any())
+			->method('isTrustedServer')->with($url)->willReturn($isTrustedServer);
+		$ocsAuthApi->expects($this->any())
+			->method('isValidToken')->with($url, $token)->willReturn($isValidToken);
+
+		if($expected === Http::STATUS_OK) {
+			$this->secureRandom->expects($this->once())->method('getMediumStrengthGenerator')
+				->willReturn($this->secureRandom);
+			$this->secureRandom->expects($this->once())->method('generate')->with(32)
+				->willReturn('secret');
+			$this->trustedServers->expects($this->once())
+				->method('addSharedSecret')->willReturn($url, 'secret');
+			$this->dbHandler->expects($this->once())
+				->method('addToken')->with($url, '');
+		} else {
+			$this->secureRandom->expects($this->never())->method('getMediumStrengthGenerator');
+			$this->secureRandom->expects($this->never())->method('generate');
+			$this->trustedServers->expects($this->never())->method('addSharedSecret');
+			$this->dbHandler->expects($this->never())->method('addToken');
+		}
+
+		$result = $ocsAuthApi->getSharedSecret();
+
+		$this->assertSame($expected, $result->getStatusCode());
+
+		if ($expected === Http::STATUS_OK) {
+			$data =  $result->getData();
+			$this->assertSame('secret', $data['sharedSecret']);
+		}
+	}
+
+	public function dataTestGetSharedSecret() {
+		return [
+			[true, true, Http::STATUS_OK],
+			[false, true, Http::STATUS_FORBIDDEN],
+			[true, false, Http::STATUS_FORBIDDEN],
+			[false, false, Http::STATUS_FORBIDDEN],
+		];
+	}
+
+}
diff --git a/apps/federation/tests/backgroundjob/getsharedsecrettest.php b/apps/federation/tests/backgroundjob/getsharedsecrettest.php
new file mode 100644
index 0000000000000000000000000000000000000000..953af5ff3e103d2fd3c679dd39278e110e9598cc
--- /dev/null
+++ b/apps/federation/tests/backgroundjob/getsharedsecrettest.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\BackgroundJob;
+
+
+use OCA\Federation\BackgroundJob\GetSharedSecret;
+use OCA\Files_Sharing\Tests\TestCase;
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\AppFramework\Http;
+use OCP\BackgroundJob\IJobList;
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+
+class GetSharedSecretTest extends TestCase {
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IClient */
+	private $httpClient;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IJobList */
+	private $jobList;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IURLGenerator */
+	private $urlGenerator;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers  */
+	private $trustedServers;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | DbHandler */
+	private $dbHandler;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | ILogger */
+	private $logger;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IResponse */
+	private $response;
+
+	/** @var GetSharedSecret */
+	private $getSharedSecret;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->httpClient = $this->getMock('OCP\Http\Client\IClient');
+		$this->jobList = $this->getMock('OCP\BackgroundJob\IJobList');
+		$this->urlGenerator = $this->getMock('OCP\IURLGenerator');
+		$this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->disableOriginalConstructor()->getMock();
+		$this->dbHandler = $this->getMockBuilder('OCA\Federation\DbHandler')
+			->disableOriginalConstructor()->getMock();
+		$this->logger = $this->getMock('OCP\ILogger');
+		$this->response = $this->getMock('OCP\Http\Client\IResponse');
+
+		$this->getSharedSecret = new GetSharedSecret(
+			$this->httpClient,
+			$this->urlGenerator,
+			$this->jobList,
+			$this->trustedServers,
+			$this->logger,
+			$this->dbHandler
+		);
+	}
+
+	/**
+	 * @dataProvider dataTestExecute
+	 *
+	 * @param bool $isTrustedServer
+	 */
+	public function testExecute($isTrustedServer) {
+		/** @var GetSharedSecret |\PHPUnit_Framework_MockObject_MockObject $getSharedSecret */
+		$getSharedSecret = $this->getMockBuilder('OCA\Federation\BackgroundJob\GetSharedSecret')
+			->setConstructorArgs(
+				[
+					$this->httpClient,
+					$this->urlGenerator,
+					$this->jobList,
+					$this->trustedServers,
+					$this->logger,
+					$this->dbHandler
+				]
+			)->setMethods(['parentExecute'])->getMock();
+		$this->invokePrivate($getSharedSecret, 'argument', [['url' => 'url']]);
+
+		$this->jobList->expects($this->once())->method('remove');
+		$this->trustedServers->expects($this->once())->method('isTrustedServer')
+			->with('url')->willReturn($isTrustedServer);
+		if ($isTrustedServer) {
+			$getSharedSecret->expects($this->once())->method('parentExecute');
+		} else {
+			$getSharedSecret->expects($this->never())->method('parentExecute');
+		}
+
+		$getSharedSecret->execute($this->jobList);
+
+	}
+
+	public function dataTestExecute() {
+		return [
+			[true],
+			[false]
+		];
+	}
+
+	/**
+	 * @dataProvider dataTestRun
+	 *
+	 * @param int $statusCode
+	 */
+	public function testRun($statusCode) {
+
+		$target = 'targetURL';
+		$source = 'sourceURL';
+		$token = 'token';
+
+		$argument = ['url' => $target, 'token' => $token];
+
+		$this->urlGenerator->expects($this->once())->method('getAbsoluteURL')->with('/')
+			->willReturn($source);
+		$this->httpClient->expects($this->once())->method('get')
+			->with(
+				$target . '/ocs/v2.php/apps/federation/api/v1/shared-secret?format=json',
+				[
+					'query' =>
+						[
+							'url' => $source,
+							'token' => $token
+						],
+					'timeout' => 3,
+					'connect_timeout' => 3,
+				]
+			)->willReturn($this->response);
+
+		$this->response->expects($this->once())->method('getStatusCode')
+			->willReturn($statusCode);
+
+		if (
+			$statusCode !== Http::STATUS_OK
+			&& $statusCode !== Http::STATUS_FORBIDDEN
+		) {
+			$this->jobList->expects($this->once())->method('add')
+				->with('OCA\Federation\BackgroundJob\GetSharedSecret', $argument);
+			$this->dbHandler->expects($this->never())->method('addToken');
+		}  else {
+			$this->dbHandler->expects($this->once())->method('addToken')->with($target, '');
+			$this->jobList->expects($this->never())->method('add');
+		}
+
+		if ($statusCode === Http::STATUS_OK) {
+			$this->response->expects($this->once())->method('getBody')
+				->willReturn('{"ocs":{"data":{"sharedSecret":"secret"}}}');
+			$this->trustedServers->expects($this->once())->method('addSharedSecret')
+				->with($target, 'secret');
+		} else {
+			$this->trustedServers->expects($this->never())->method('addSharedSecret');
+		}
+
+		$this->invokePrivate($this->getSharedSecret, 'run', [$argument]);
+	}
+
+	public function dataTestRun() {
+		return [
+			[Http::STATUS_OK],
+			[Http::STATUS_FORBIDDEN],
+			[Http::STATUS_CONFLICT],
+		];
+	}
+
+}
diff --git a/apps/federation/tests/backgroundjob/requestsharedsecrettest.php b/apps/federation/tests/backgroundjob/requestsharedsecrettest.php
new file mode 100644
index 0000000000000000000000000000000000000000..df81113c686ea776165d043e104afa718d16f96b
--- /dev/null
+++ b/apps/federation/tests/backgroundjob/requestsharedsecrettest.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\BackgroundJob;
+
+
+use OCA\Federation\BackgroundJob\RequestSharedSecret;
+use OCP\AppFramework\Http;
+use Test\TestCase;
+
+class RequestSharedSecretTest extends TestCase {
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IClient */
+	private $httpClient;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IJobList */
+	private $jobList;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IURLGenerator */
+	private $urlGenerator;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | DbHandler */
+	private $dbHandler;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers */
+	private $trustedServers;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IResponse */
+	private $response;
+
+	/** @var  RequestSharedSecret */
+	private $requestSharedSecret;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->httpClient = $this->getMock('OCP\Http\Client\IClient');
+		$this->jobList = $this->getMock('OCP\BackgroundJob\IJobList');
+		$this->urlGenerator = $this->getMock('OCP\IURLGenerator');
+		$this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->disableOriginalConstructor()->getMock();
+		$this->dbHandler = $this->getMockBuilder('OCA\Federation\DbHandler')
+			->disableOriginalConstructor()->getMock();
+		$this->response = $this->getMock('OCP\Http\Client\IResponse');
+
+		$this->requestSharedSecret = new RequestSharedSecret(
+			$this->httpClient,
+			$this->urlGenerator,
+			$this->jobList,
+			$this->trustedServers,
+			$this->dbHandler
+		);
+	}
+
+	/**
+	 * @dataProvider dataTestExecute
+	 *
+	 * @param bool $isTrustedServer
+	 */
+	public function testExecute($isTrustedServer) {
+		/** @var RequestSharedSecret |\PHPUnit_Framework_MockObject_MockObject $requestSharedSecret */
+		$requestSharedSecret = $this->getMockBuilder('OCA\Federation\BackgroundJob\RequestSharedSecret')
+			->setConstructorArgs(
+				[
+					$this->httpClient,
+					$this->urlGenerator,
+					$this->jobList,
+					$this->trustedServers,
+					$this->dbHandler
+				]
+			)->setMethods(['parentExecute'])->getMock();
+		$this->invokePrivate($requestSharedSecret, 'argument', [['url' => 'url']]);
+
+		$this->jobList->expects($this->once())->method('remove');
+		$this->trustedServers->expects($this->once())->method('isTrustedServer')
+			->with('url')->willReturn($isTrustedServer);
+		if ($isTrustedServer) {
+			$requestSharedSecret->expects($this->once())->method('parentExecute');
+		} else {
+			$requestSharedSecret->expects($this->never())->method('parentExecute');
+		}
+
+		$requestSharedSecret->execute($this->jobList);
+
+	}
+
+	public function dataTestExecute() {
+		return [
+			[true],
+			[false]
+		];
+	}
+
+	/**
+	 * @dataProvider dataTestRun
+	 *
+	 * @param int $statusCode
+	 */
+	public function testRun($statusCode) {
+
+		$target = 'targetURL';
+		$source = 'sourceURL';
+		$token = 'token';
+
+		$argument = ['url' => $target, 'token' => $token];
+
+		$this->urlGenerator->expects($this->once())->method('getAbsoluteURL')->with('/')
+			->willReturn($source);
+		$this->httpClient->expects($this->once())->method('post')
+			->with(
+				$target . '/ocs/v2.php/apps/federation/api/v1/request-shared-secret?format=json',
+				[
+					'body' =>
+						[
+							'url' => $source,
+							'token' => $token
+						],
+					'timeout' => 3,
+					'connect_timeout' => 3,
+				]
+			)->willReturn($this->response);
+
+		$this->response->expects($this->once())->method('getStatusCode')
+			->willReturn($statusCode);
+
+		if (
+			$statusCode !== Http::STATUS_OK
+			&& $statusCode !== Http::STATUS_FORBIDDEN
+		) {
+			$this->jobList->expects($this->once())->method('add')
+				->with('OCA\Federation\BackgroundJob\RequestSharedSecret', $argument);
+			$this->dbHandler->expects($this->never())->method('addToken');
+		}
+
+		if ($statusCode === Http::STATUS_FORBIDDEN) {
+			$this->jobList->expects($this->never())->method('add');
+			$this->dbHandler->expects($this->once())->method('addToken')->with($target, '');
+		}
+
+		$this->invokePrivate($this->requestSharedSecret, 'run', [$argument]);
+	}
+
+	public function dataTestRun() {
+		return [
+			[Http::STATUS_OK],
+			[Http::STATUS_FORBIDDEN],
+			[Http::STATUS_CONFLICT],
+		];
+	}
+}
diff --git a/apps/federation/tests/controller/settingscontrollertest.php b/apps/federation/tests/controller/settingscontrollertest.php
new file mode 100644
index 0000000000000000000000000000000000000000..efbc6911c52acbd3ed95f0a6c439802a82cab41e
--- /dev/null
+++ b/apps/federation/tests/controller/settingscontrollertest.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\Controller;
+
+
+use OCA\Federation\Controller\SettingsController;
+use OCP\AppFramework\Http\DataResponse;
+use Test\TestCase;
+
+class SettingsControllerTest extends TestCase {
+
+	/** @var SettingsController  */
+	private $controller;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IRequest */
+	private $request;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IL10N */
+	private $l10n;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | \OCA\Federation\TrustedServers */
+	private $trustedServers;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->request = $this->getMock('OCP\IRequest');
+		$this->l10n = $this->getMock('OCP\IL10N');
+		$this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->disableOriginalConstructor()->getMock();
+
+		$this->controller = new SettingsController(
+			'SettingsControllerTest',
+			$this->request,
+			$this->l10n,
+			$this->trustedServers
+		);
+	}
+
+	public function testAddServer() {
+		$this->trustedServers
+			->expects($this->once())
+			->method('isTrustedServer')
+			->with('url')
+			->willReturn(false);
+		$this->trustedServers
+			->expects($this->once())
+			->method('isOwnCloudServer')
+			->with('url')
+			->willReturn(true);
+
+		$result = $this->controller->addServer('url');
+		$this->assertTrue($result instanceof DataResponse);
+
+		$data = $result->getData();
+		$this->assertSame(200, $result->getStatus());
+		$this->assertSame('url', $data['url']);
+		$this->assertArrayHasKey('id', $data);
+	}
+
+	/**
+	 * @dataProvider checkServerFails
+	 * @expectedException \OC\HintException
+	 *
+	 * @param bool $isTrustedServer
+	 * @param bool $isOwnCloud
+	 */
+	public function testAddServerFail($isTrustedServer, $isOwnCloud) {
+		$this->trustedServers
+			->expects($this->any())
+			->method('isTrustedServer')
+			->with('url')
+			->willReturn($isTrustedServer);
+		$this->trustedServers
+			->expects($this->any())
+			->method('isOwnCloudServer')
+			->with('url')
+			->willReturn($isOwnCloud);
+
+		$this->controller->addServer('url');
+	}
+
+	public function testRemoveServer() {
+		$this->trustedServers->expects($this->once())->method('removeServer')
+		->with('url');
+		$result = $this->controller->removeServer('url');
+		$this->assertTrue($result instanceof DataResponse);
+		$this->assertSame(200, $result->getStatus());
+	}
+
+	public function testCheckServer() {
+		$this->trustedServers
+			->expects($this->once())
+			->method('isTrustedServer')
+			->with('url')
+			->willReturn(false);
+		$this->trustedServers
+			->expects($this->once())
+			->method('isOwnCloudServer')
+			->with('url')
+			->willReturn(true);
+
+		$this->assertTrue(
+			$this->invokePrivate($this->controller, 'checkServer', ['url'])
+		);
+
+	}
+
+	/**
+	 * @dataProvider checkServerFails
+	 * @expectedException \OC\HintException
+	 *
+	 * @param bool $isTrustedServer
+	 * @param bool $isOwnCloud
+	 */
+	public function testCheckServerFail($isTrustedServer, $isOwnCloud) {
+		$this->trustedServers
+			->expects($this->any())
+			->method('isTrustedServer')
+			->with('url')
+			->willReturn($isTrustedServer);
+		$this->trustedServers
+			->expects($this->any())
+			->method('isOwnCloudServer')
+			->with('url')
+			->willReturn($isOwnCloud);
+
+		$this->assertTrue(
+			$this->invokePrivate($this->controller, 'checkServer', ['url'])
+		);
+
+	}
+
+	/**
+	 * data to simulate checkServer fails
+	 *
+	 * @return array
+	 */
+	public function checkServerFails() {
+		return [
+			[true, true],
+			[false, false]
+		];
+	}
+
+}
diff --git a/apps/federation/tests/lib/dbhandlertest.php b/apps/federation/tests/lib/dbhandlertest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e47df092f8c145404bf8d3b59d33dca755377b2b
--- /dev/null
+++ b/apps/federation/tests/lib/dbhandlertest.php
@@ -0,0 +1,243 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\lib;
+
+
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\IDBConnection;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class DbHandlerTest extends TestCase {
+
+	/** @var  DbHandler */
+	private $dbHandler;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject */
+	private $il10n;
+
+	/** @var  IDBConnection */
+	private $connection;
+
+	/** @var string  */
+	private $dbTable = 'trusted_servers';
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->connection = \OC::$server->getDatabaseConnection();
+		$this->il10n = $this->getMock('OCP\IL10N');
+
+		$this->dbHandler = new DbHandler(
+			$this->connection,
+			$this->il10n
+		);
+
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertEmpty($result, 'we need to start with a empty trusted_servers table');
+	}
+
+	public function tearDown() {
+		parent::tearDown();
+		$query = $this->connection->getQueryBuilder()->delete($this->dbTable);
+		$query->execute();
+	}
+
+	public function testAddServer() {
+		$id = $this->dbHandler->addServer('server1');
+
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame('server1', $result[0]['url']);
+		$this->assertSame($id, (int)$result[0]['id']);
+		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
+	}
+
+	public function testRemove() {
+		$id1 = $this->dbHandler->addServer('server1');
+		$id2 = $this->dbHandler->addServer('server2');
+
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(2, count($result));
+		$this->assertSame('server1', $result[0]['url']);
+		$this->assertSame('server2', $result[1]['url']);
+		$this->assertSame($id1, (int)$result[0]['id']);
+		$this->assertSame($id2, (int)$result[1]['id']);
+
+		$this->dbHandler->removeServer($id2);
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame('server1', $result[0]['url']);
+		$this->assertSame($id1, (int)$result[0]['id']);
+	}
+
+	public function testGetAll() {
+		$id1 = $this->dbHandler->addServer('server1');
+		$id2 = $this->dbHandler->addServer('server2');
+
+		$result = $this->dbHandler->getAllServer();
+		$this->assertSame(2, count($result));
+		$this->assertSame('server1', $result[0]['url']);
+		$this->assertSame('server2', $result[1]['url']);
+		$this->assertSame($id1, (int)$result[0]['id']);
+		$this->assertSame($id2, (int)$result[1]['id']);
+	}
+
+	/**
+	 * @dataProvider dataTestServerExists
+	 *
+	 * @param string $serverInTable
+	 * @param string $checkForServer
+	 * @param bool $expected
+	 */
+	public function testServerExists($serverInTable, $checkForServer, $expected) {
+		$this->dbHandler->addServer($serverInTable);
+		$this->assertSame($expected,
+			$this->dbHandler->serverExists($checkForServer)
+		);
+	}
+
+	public function dataTestServerExists() {
+		return [
+			['server1', 'server1', true],
+			['server1', 'http://server1', true],
+			['server1', 'server2', false]
+		];
+	}
+
+	public function testAddToken() {
+		$this->dbHandler->addServer('server1');
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame(null, $result[0]['token']);
+		$this->dbHandler->addToken('http://server1', 'token');
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame('token', $result[0]['token']);
+	}
+
+	public function testGetToken() {
+		$this->dbHandler->addServer('server1');
+		$this->dbHandler->addToken('http://server1', 'token');
+		$this->assertSame('token',
+			$this->dbHandler->getToken('https://server1')
+		);
+	}
+
+	public function testAddSharedSecret() {
+		$this->dbHandler->addServer('server1');
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame(null, $result[0]['shared_secret']);
+		$this->dbHandler->addSharedSecret('http://server1', 'secret');
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame('secret', $result[0]['shared_secret']);
+	}
+
+	public function testGetSharedSecret() {
+		$this->dbHandler->addServer('server1');
+		$this->dbHandler->addSharedSecret('http://server1', 'secret');
+		$this->assertSame('secret',
+			$this->dbHandler->getSharedSecret('https://server1')
+		);
+	}
+
+	public function testSetServerStatus() {
+		$this->dbHandler->addServer('server1');
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']);
+		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
+		$query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable);
+		$result = $query->execute()->fetchAll();
+		$this->assertSame(1, count($result));
+		$this->assertSame(TrustedServers::STATUS_OK, (int)$result[0]['status']);
+	}
+
+	public function testGetServerStatus() {
+		$this->dbHandler->addServer('server1');
+		$this->dbHandler->setServerStatus('http://server1', TrustedServers::STATUS_OK);
+		$this->assertSame(TrustedServers::STATUS_OK,
+			$this->dbHandler->getServerStatus('https://server1')
+		);
+	}
+
+	/**
+	 * hash should always be computed with the normalized URL
+	 *
+	 * @dataProvider dataTestHash
+	 *
+	 * @param string $url
+	 * @param string $expected
+	 */
+	public function testHash($url, $expected) {
+		$this->assertSame($expected,
+			$this->invokePrivate($this->dbHandler, 'hash', [$url])
+		);
+	}
+
+	public function dataTestHash() {
+		return [
+			['server1', md5('server1')],
+			['http://server1', md5('server1')],
+			['https://server1', md5('server1')],
+			['http://server1/', md5('server1')],
+		];
+	}
+
+	/**
+	 * @dataProvider dataTestNormalizeUrl
+	 *
+	 * @param string $url
+	 * @param string $expected
+	 */
+	public function testNormalizeUrl($url, $expected) {
+		$this->assertSame($expected,
+			$this->invokePrivate($this->dbHandler, 'normalizeUrl', [$url])
+		);
+	}
+
+	public function dataTestNormalizeUrl() {
+		return [
+			['owncloud.org', 'owncloud.org'],
+			['http://owncloud.org', 'owncloud.org'],
+			['https://owncloud.org', 'owncloud.org'],
+			['https://owncloud.org//mycloud', 'owncloud.org/mycloud'],
+			['https://owncloud.org/mycloud/', 'owncloud.org/mycloud'],
+		];
+	}
+
+}
diff --git a/apps/federation/tests/lib/trustedserverstest.php b/apps/federation/tests/lib/trustedserverstest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d067cd1c1857f2b5057390181d3c33e8e699ab3b
--- /dev/null
+++ b/apps/federation/tests/lib/trustedserverstest.php
@@ -0,0 +1,344 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\lib;
+
+
+use OCA\Federation\DbHandler;
+use OCA\Federation\TrustedServers;
+use OCP\BackgroundJob\IJobList;
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IClientService;
+use OCP\Http\Client\IResponse;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\Security\ISecureRandom;
+use Test\TestCase;
+
+class TrustedServersTest extends TestCase {
+
+	/** @var  TrustedServers */
+	private $trustedServers;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | DbHandler */
+	private $dbHandler;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | IClientService */
+	private $httpClientService;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | IClient */
+	private $httpClient;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | IResponse */
+	private $response;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | ILogger */
+	private $logger;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | IJobList */
+	private $jobList;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | ISecureRandom */
+	private $secureRandom;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | IConfig */
+	private $config;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->dbHandler = $this->getMockBuilder('\OCA\Federation\DbHandler')
+			->disableOriginalConstructor()->getMock();
+		$this->httpClientService = $this->getMock('OCP\Http\Client\IClientService');
+		$this->httpClient = $this->getMock('OCP\Http\Client\IClient');
+		$this->response = $this->getMock('OCP\Http\Client\IResponse');
+		$this->logger = $this->getMock('OCP\ILogger');
+		$this->jobList = $this->getMock('OCP\BackgroundJob\IJobList');
+		$this->secureRandom = $this->getMock('OCP\Security\ISecureRandom');
+		$this->config = $this->getMock('OCP\IConfig');
+
+		$this->trustedServers = new TrustedServers(
+			$this->dbHandler,
+			$this->httpClientService,
+			$this->logger,
+			$this->jobList,
+			$this->secureRandom,
+			$this->config
+		);
+
+	}
+
+	/**
+	 * @dataProvider dataTrueFalse
+	 *
+	 * @param bool $success
+	 */
+	public function testAddServer($success) {
+		/** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers $trustedServer */
+		$trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->setConstructorArgs(
+				[
+					$this->dbHandler,
+					$this->httpClientService,
+					$this->logger,
+					$this->jobList,
+					$this->secureRandom,
+					$this->config
+				]
+			)
+			->setMethods(['normalizeUrl', 'updateProtocol'])
+			->getMock();
+		$trustedServers->expects($this->once())->method('updateProtocol')
+				->with('url')->willReturn('https://url');
+		$this->dbHandler->expects($this->once())->method('addServer')->with('https://url')
+			->willReturn($success);
+
+		if ($success) {
+			$this->secureRandom->expects($this->once())->method('getMediumStrengthGenerator')
+				->willReturn($this->secureRandom);
+			$this->secureRandom->expects($this->once())->method('generate')
+				->willReturn('token');
+			$this->dbHandler->expects($this->once())->method('addToken')->with('https://url', 'token');
+			$this->jobList->expects($this->once())->method('add')
+				->with('OCA\Federation\BackgroundJob\RequestSharedSecret',
+						['url' => 'https://url', 'token' => 'token']);
+		} else {
+			$this->jobList->expects($this->never())->method('add');
+		}
+
+		$this->assertSame($success,
+			$trustedServers->addServer('url')
+		);
+	}
+
+	public function dataTrueFalse() {
+		return [
+			[true],
+			[false]
+		];
+	}
+
+	/**
+	 * @dataProvider dataTrueFalse
+	 *
+	 * @param bool $status
+	 */
+	public function testSetAutoAddServers($status) {
+		if ($status) {
+			$this->config->expects($this->once())->method('setAppValue')
+				->with('federation', 'autoAddServers', '1');
+		} else {
+			$this->config->expects($this->once())->method('setAppValue')
+				->with('federation', 'autoAddServers', '0');
+		}
+
+		$this->trustedServers->setAutoAddServers($status);
+	}
+
+	/**
+	 * @dataProvider dataTestGetAutoAddServers
+	 *
+	 * @param string $status
+	 * @param bool $expected
+	 */
+	public function testGetAutoAddServers($status, $expected) {
+		$this->config->expects($this->once())->method('getAppValue')
+			->with('federation', 'autoAddServers', '1')->willReturn($status);
+
+		$this->assertSame($expected,
+			$this->trustedServers->getAutoAddServers($status)
+		);
+	}
+
+	public function dataTestGetAutoAddServers() {
+		return [
+			['1', true],
+			['0', false]
+		];
+	}
+
+	public function testAddSharedSecret() {
+		$this->dbHandler->expects($this->once())->method('addSharedSecret')
+			->with('url', 'secret');
+		$this->trustedServers->addSharedSecret('url', 'secret');
+	}
+
+	public function testGetSharedSecret() {
+		$this->dbHandler->expects($this->once())->method('getSharedSecret')
+			->with('url')->willReturn(true);
+		$this->assertTrue(
+			$this->trustedServers->getSharedSecret('url')
+		);
+	}
+
+	public function testRemoveServer() {
+		$id = 42;
+		$this->dbHandler->expects($this->once())->method('removeServer')->with($id);
+		$this->trustedServers->removeServer($id);
+	}
+
+	public function testGetServers() {
+		$this->dbHandler->expects($this->once())->method('getAllServer')->willReturn(true);
+
+		$this->assertTrue(
+			$this->trustedServers->getServers()
+		);
+	}
+
+
+	public function testIsTrustedServer() {
+		$this->dbHandler->expects($this->once())->method('serverExists')->with('url')
+			->willReturn(true);
+
+		$this->assertTrue(
+			$this->trustedServers->isTrustedServer('url')
+		);
+	}
+
+	public function testSetServerStatus() {
+		$this->dbHandler->expects($this->once())->method('setServerStatus')
+			->with('url', 'status');
+		$this->trustedServers->setServerStatus('url', 'status');
+	}
+
+	public function testGetServerStatus() {
+		$this->dbHandler->expects($this->once())->method('getServerStatus')
+			->with('url')->willReturn(true);
+		$this->assertTrue(
+			$this->trustedServers->getServerStatus('url')
+		);
+	}
+
+	/**
+	 * @dataProvider dataTestIsOwnCloudServer
+	 *
+	 * @param int $statusCode
+	 * @param bool $isValidOwnCloudVersion
+	 * @param bool $expected
+	 */
+	public function testIsOwnCloudServer($statusCode, $isValidOwnCloudVersion, $expected) {
+
+		$server = 'server1';
+
+		/** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers $trustedServer */
+		$trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers')
+			->setConstructorArgs(
+				[
+					$this->dbHandler,
+					$this->httpClientService,
+					$this->logger,
+					$this->jobList,
+					$this->secureRandom,
+					$this->config
+				]
+			)
+			->setMethods(['checkOwnCloudVersion'])
+			->getMock();
+
+		$this->httpClientService->expects($this->once())->method('newClient')
+			->willReturn($this->httpClient);
+
+		$this->httpClient->expects($this->once())->method('get')->with($server . '/status.php')
+			->willReturn($this->response);
+
+		$this->response->expects($this->once())->method('getStatusCode')
+			->willReturn($statusCode);
+
+		if ($statusCode === 200) {
+			$trustedServers->expects($this->once())->method('checkOwnCloudVersion')
+				->willReturn($isValidOwnCloudVersion);
+		} else {
+			$trustedServers->expects($this->never())->method('checkOwnCloudVersion');
+		}
+
+		$this->assertSame($expected,
+			$trustedServers->isOwnCloudServer($server)
+		);
+
+	}
+
+	public function dataTestIsOwnCloudServer() {
+		return [
+			[200, true, true],
+			[200, false, false],
+			[404, true, false],
+		];
+	}
+
+	public function testIsOwnCloudServerFail() {
+		$server = 'server1';
+
+		$this->httpClientService->expects($this->once())->method('newClient')
+			->willReturn($this->httpClient);
+
+		$this->logger->expects($this->once())->method('error')
+			->with('simulated exception', ['app' => 'federation']);
+
+		$this->httpClient->expects($this->once())->method('get')->with($server . '/status.php')
+			->willReturnCallback(function () {
+				throw new \Exception('simulated exception');
+			});
+
+		$this->assertFalse($this->trustedServers->isOwnCloudServer($server));
+
+	}
+
+	/**
+	 * @dataProvider dataTestCheckOwnCloudVersion
+	 *
+	 * @param $statusphp
+	 * @param $expected
+	 */
+	public function testCheckOwnCloudVersion($statusphp, $expected) {
+		$this->assertSame($expected,
+			$this->invokePrivate($this->trustedServers, 'checkOwnCloudVersion', [$statusphp])
+		);
+	}
+
+	public function dataTestCheckOwnCloudVersion() {
+		return [
+			['{"version":"8.4.0"}', false],
+			['{"version":"9.0.0"}', true],
+			['{"version":"9.1.0"}', true]
+		];
+	}
+
+	/**
+	 * @dataProvider dataTestUpdateProtocol
+	 * @param string $url
+	 * @param string $expected
+	 */
+	public function testUpdateProtocol($url, $expected) {
+		$this->assertSame($expected,
+			$this->invokePrivate($this->trustedServers, 'updateProtocol', [$url])
+		);
+	}
+
+	public function dataTestUpdateProtocol() {
+		return [
+			['http://owncloud.org', 'http://owncloud.org'],
+			['https://owncloud.org', 'https://owncloud.org'],
+			['owncloud.org', 'https://owncloud.org'],
+			['httpserver', 'https://httpserver'],
+		];
+	}
+}
diff --git a/apps/federation/tests/middleware/addservermiddlewaretest.php b/apps/federation/tests/middleware/addservermiddlewaretest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1be5a34228563e8f8f21217a6594c834de02c379
--- /dev/null
+++ b/apps/federation/tests/middleware/addservermiddlewaretest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+namespace OCA\Federation\Tests\Middleware;
+
+
+use OC\HintException;
+use OCA\Federation\Middleware\AddServerMiddleware;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http;
+use Test\TestCase;
+
+class AddServerMiddlewareTest extends TestCase {
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | ILogger */
+	private $logger;
+
+	/** @var \PHPUnit_Framework_MockObject_MockObject | \OCP\IL10N */
+	private $l10n;
+
+	/** @var  AddServerMiddleware */
+	private $middleware;
+
+	/** @var  \PHPUnit_Framework_MockObject_MockObject | Controller */
+	private $controller;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->logger = $this->getMock('OCP\ILogger');
+		$this->l10n = $this->getMock('OCP\IL10N');
+		$this->controller = $this->getMockBuilder('OCP\AppFramework\Controller')
+			->disableOriginalConstructor()->getMock();
+
+		$this->middleware = new AddServerMiddleware(
+			'AddServerMiddlewareTest',
+			$this->l10n,
+			$this->logger
+		);
+	}
+
+	/**
+	 * @dataProvider dataTestAfterException
+	 *
+	 * @param \Exception $exception
+	 * @param string $message
+	 * @param string $hint
+	 */
+	public function testAfterException($exception, $message, $hint) {
+
+		$this->logger->expects($this->once())->method('error')
+			->with($message, ['app' => 'AddServerMiddlewareTest']);
+
+		$this->l10n->expects($this->any())->method('t')
+			->willReturnCallback(
+				function($message) {
+					return $message;
+				}
+			);
+
+		$result = $this->middleware->afterException($this->controller, 'method', $exception);
+
+		$this->assertSame(Http::STATUS_BAD_REQUEST,
+			$result->getStatus()
+		);
+
+		$data = $result->getData();
+
+		$this->assertSame($hint,
+			$data['message']
+		);
+	}
+
+	public function dataTestAfterException() {
+		return [
+			[new HintException('message', 'hint'), 'message', 'hint'],
+			[new \Exception('message'), 'message', 'Unknown error'],
+		];
+	}
+
+}
diff --git a/core/shipped.json b/core/shipped.json
index cd1fca4d9fe2670289e84edec42463798154cb65..a7466a41aef7f5a3c4125ebbc9a1e7570b767736 100644
--- a/core/shipped.json
+++ b/core/shipped.json
@@ -32,7 +32,8 @@
     "user_ldap",
     "user_shibboleth",
     "windows_network_drive",
-    "password_policy"
+    "password_policy",
+    "federation"
   ],
   "alwaysEnabled": [
     "files",
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 60ba805d3ca0eef4c8c7d664445231f9b175ea56..5a1d864734b9a8664ad1844c22ed06f02a996f34 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -412,13 +412,13 @@ table.grid td.date{
 .cronlog {
 	margin-left: 10px;
 }
-.cronstatus {
+.status {
 	display: inline-block;
 	height: 16px;
 	width: 16px;
 	vertical-align: text-bottom;
 }
-.cronstatus.success {
+.status.success {
 	border-radius: 50%;
 }
 
diff --git a/settings/templates/admin.php b/settings/templates/admin.php
index 24af49642482b657648dc595ec2fb2ccd55f817d..0721c0e0afbed36829f6d38083534450da99adf5 100644
--- a/settings/templates/admin.php
+++ b/settings/templates/admin.php
@@ -290,18 +290,18 @@ if ($_['cronErrors']) {
 			$relative_time = relative_modified_date($_['lastcron']);
 			$absolute_time = OC_Util::formatDate($_['lastcron']);
 			if (time() - $_['lastcron'] <= 3600): ?>
-				<span class="cronstatus success"></span>
+				<span class="status success"></span>
 				<span class="crondate" original-title="<?php p($absolute_time);?>">
 					<?php p($l->t("Last cron job execution: %s.", [$relative_time]));?>
 				</span>
 			<?php else: ?>
-				<span class="cronstatus error"></span>
+				<span class="status error"></span>
 				<span class="crondate" original-title="<?php p($absolute_time);?>">
 					<?php p($l->t("Last cron job execution: %s. Something seems wrong.", [$relative_time]));?>
 				</span>
 			<?php endif;
 		else: ?>
-			<span class="cronstatus error"></span>
+			<span class="status error"></span>
 			<?php p($l->t("Cron was not executed yet!"));
 		endif; ?>
 	</p>
diff --git a/tests/enable_all.php b/tests/enable_all.php
index 464155b1f39abe79e5319cee501ae9d7e4c36158..6f2d1fa87173cb1d06491b56c69fbfccd231e5e1 100644
--- a/tests/enable_all.php
+++ b/tests/enable_all.php
@@ -22,4 +22,4 @@ enableApp('encryption');
 enableApp('user_ldap');
 enableApp('files_versions');
 enableApp('provisioning_api');
-
+enableApp('federation');