diff --git a/lib/private/group.php b/lib/private/group.php
index 49f683c411aa124a9e0167e5dcc95ea8acef32a0..d6e6e17f881c0962b6f34ef58a1e4f94f0c92573 100644
--- a/lib/private/group.php
+++ b/lib/private/group.php
@@ -37,6 +37,7 @@ class OC_Group {
 
 	/**
 	 * @return \OC\Group\Manager
+	 * @deprecated Use \OC::$server->getGroupManager();
 	 */
 	public static function getManager() {
 		return \OC::$server->getGroupManager();
@@ -44,6 +45,7 @@ class OC_Group {
 
 	/**
 	 * @return \OC\User\Manager
+	 * @deprecated Use \OC::$server->getUserManager()
 	 */
 	private static function getUserManager() {
 		return \OC::$server->getUserManager();
@@ -73,12 +75,10 @@ class OC_Group {
 	 *
 	 * Tries to create a new group. If the group name already exists, false will
 	 * be returned. Basic checking of Group name
+	 * @deprecated Use \OC::$server->getGroupManager()->createGroup() instead
 	 */
 	public static function createGroup($gid) {
-		OC_Hook::emit("OC_Group", "pre_createGroup", array("run" => true, "gid" => $gid));
-
 		if (self::getManager()->createGroup($gid)) {
-			OC_Hook::emit("OC_User", "post_createGroup", array("gid" => $gid));
 			return true;
 		} else {
 			return false;
@@ -91,19 +91,12 @@ class OC_Group {
 	 * @return bool
 	 *
 	 * Deletes a group and removes it from the group_user-table
+	 * @deprecated Use \OC::$server->getGroupManager()->delete() instead
 	 */
 	public static function deleteGroup($gid) {
-		// Prevent users from deleting group admin
-		if ($gid == "admin") {
-			return false;
-		}
-
-		OC_Hook::emit("OC_Group", "pre_deleteGroup", array("run" => true, "gid" => $gid));
-
 		$group = self::getManager()->get($gid);
 		if ($group) {
 			if ($group->delete()) {
-				OC_Hook::emit("OC_User", "post_deleteGroup", array("gid" => $gid));
 				return true;
 			}
 		}
@@ -117,6 +110,7 @@ class OC_Group {
 	 * @return bool
 	 *
 	 * Checks whether the user is member of a group or not.
+	 * @deprecated Use \OC::$server->getGroupManager->inGroup($user);
 	 */
 	public static function inGroup($uid, $gid) {
 		$group = self::getManager()->get($gid);
@@ -134,14 +128,13 @@ class OC_Group {
 	 * @return bool
 	 *
 	 * Adds a user to a group.
+	 * @deprecated Use \OC::$server->getGroupManager->addUser();
 	 */
 	public static function addToGroup($uid, $gid) {
 		$group = self::getManager()->get($gid);
 		$user = self::getUserManager()->get($uid);
 		if ($group and $user) {
-			OC_Hook::emit("OC_Group", "pre_addToGroup", array("run" => true, "uid" => $uid, "gid" => $gid));
 			$group->addUser($user);
-			OC_Hook::emit("OC_User", "post_addToGroup", array("uid" => $uid, "gid" => $gid));
 			return true;
 		} else {
 			return false;
@@ -176,6 +169,7 @@ class OC_Group {
 	 *
 	 * This function fetches all groups a user belongs to. It does not check
 	 * if the user exists at all.
+	 * @deprecated Use \OC::$server->getGroupManager->getuserGroupIds($user)
 	 */
 	public static function getUserGroups($uid) {
 		$user = self::getUserManager()->get($uid);
@@ -209,6 +203,7 @@ class OC_Group {
 	 *
 	 * @param string $gid
 	 * @return bool
+	 * @deprecated Use \OC::$server->getGroupManager->groupExists($gid)
 	 */
 	public static function groupExists($gid) {
 		return self::getManager()->groupExists($gid);
@@ -260,6 +255,7 @@ class OC_Group {
 	 * @param int $limit
 	 * @param int $offset
 	 * @return array an array of display names (value) and user ids(key)
+	 * @deprecated Use \OC::$server->getGroupManager->displayNamesInGroup($gid, $search, $limit, $offset)
 	 */
 	public static function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) {
 		return self::getManager()->displayNamesInGroup($gid, $search, $limit, $offset);
diff --git a/lib/private/group/group.php b/lib/private/group/group.php
index 6111051ea090902ca9f8041f29dc2d758b98598b..5f439e91cded211e496a401ce697933a2423db86 100644
--- a/lib/private/group/group.php
+++ b/lib/private/group/group.php
@@ -229,6 +229,11 @@ class Group implements IGroup {
 	 * @return bool
 	 */
 	public function delete() {
+		// Prevent users from deleting group admin
+		if ($this->getGID() === 'admin') {
+			return false;
+		}
+
 		$result = false;
 		if ($this->emitter) {
 			$this->emitter->emit('\OC\Group', 'preDelete', array($this));
diff --git a/lib/private/group/metadata.php b/lib/private/group/metadata.php
index 687a735347c5f4ca632705f11645d240991e72a5..c702c924ff71996ddf5cfe10ea175c23dd4196aa 100644
--- a/lib/private/group/metadata.php
+++ b/lib/private/group/metadata.php
@@ -29,7 +29,7 @@ class MetaData {
 	protected $metaData = array();
 
 	/**
-	 * @var \OC\Group\Manager $groupManager
+	 * @var \OCP\IGroupManager $groupManager
 	 */
 	protected $groupManager;
 
@@ -41,12 +41,12 @@ class MetaData {
 	/**
 	 * @param string $user the uid of the current user
 	 * @param bool $isAdmin whether the current users is an admin
-	 * @param \OC\Group\Manager $groupManager
+	 * @param \OCP\IGroupManager $groupManager
 	 */
 	public function __construct(
 			$user,
 			$isAdmin,
-			\OC\Group\Manager $groupManager
+			\OCP\IGroupManager $groupManager
 			) {
 		$this->user = $user;
 		$this->isAdmin = (bool)$isAdmin;
@@ -168,6 +168,7 @@ class MetaData {
 		if($this->isAdmin) {
 			return $this->groupManager->search($search);
 		} else {
+			// FIXME: Remove static method call
 			$groupIds = \OC_SubAdmin::getSubAdminsGroups($this->user);
 
 			/* \OC_SubAdmin::getSubAdminsGroups() returns an array of GIDs, but this
diff --git a/lib/private/server.php b/lib/private/server.php
index 7bd7f8ca45d9d0c8ba52f421080e4df87d0519db..a08014fa6fab52a8723461f8af9aabc98e0f8243 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -104,8 +104,26 @@ class Server extends SimpleContainer implements IServerContainer {
 			return new \OC\User\Manager($config);
 		});
 		$this->registerService('GroupManager', function (Server $c) {
-			$userManager = $c->getUserManager();
-			return new \OC\Group\Manager($userManager);
+			$groupManager = new \OC\Group\Manager($this->getUserManager());
+			$groupManager->listen('\OC\Group', 'preCreate', function ($gid) {
+				\OC_Hook::emit('OC_Group', 'pre_createGroup', array('run' => true, 'gid' => $gid));
+			});
+			$groupManager->listen('\OC\Group', 'postCreate', function (\OC\Group\Group $gid) {
+				\OC_Hook::emit('OC_User', 'post_createGroup', array('gid' => $gid->getGID()));
+			});
+			$groupManager->listen('\OC\Group', 'preDelete', function (\OC\Group\Group $group) {
+				\OC_Hook::emit('OC_Group', 'pre_deleteGroup', array('run' => true, 'gid' => $group->getGID()));
+			});
+			$groupManager->listen('\OC\Group', 'postDelete', function (\OC\Group\Group $group) {
+				\OC_Hook::emit('OC_User', 'post_deleteGroup', array('gid' => $group->getGID()));
+			});
+			$groupManager->listen('\OC\Group', 'preAddUser', function (\OC\Group\Group $group, \OC\User\User $user) {
+				\OC_Hook::emit('OC_Group', 'pre_addToGroup', array('run' => true, 'uid' => $user->getUID(), 'gid' => $group->getGID()));
+			});
+			$groupManager->listen('\OC\Group', 'postAddUser', function (\OC\Group\Group $group, \OC\User\User $user) {
+				\OC_Hook::emit('OC_Group', 'post_addToGroup', array('uid' => $user->getUID(), 'gid' => $group->getGID()));
+			});
+			return $groupManager;
 		});
 		$this->registerService('UserSession', function (Server $c) {
 			$manager = $c->getUserManager();
diff --git a/lib/private/user.php b/lib/private/user.php
index b2a235425c4e5cf71881813bbc0806dd229f276a..f93b76a3a648aeb3d3c9e81172b0259ab400086d 100644
--- a/lib/private/user.php
+++ b/lib/private/user.php
@@ -47,6 +47,7 @@ class OC_User {
 
 	/**
 	 * @return \OC\User\Manager
+	 * @deprecated Use \OC::$server->getUserManager()
 	 */
 	public static function getManager() {
 		return OC::$server->getUserManager();
@@ -179,6 +180,7 @@ class OC_User {
 	 * itself, not in its subclasses.
 	 *
 	 * Allowed characters in the username are: "a-z", "A-Z", "0-9" and "_.@-"
+	 * @deprecated Use \OC::$server->getUserManager->createUser($uid, $password)
 	 */
 	public static function createUser($uid, $password) {
 		return self::getManager()->createUser($uid, $password);
@@ -190,30 +192,12 @@ class OC_User {
 	 * @return bool
 	 *
 	 * Deletes a user
+	 * @deprecated Use \OC::$server->getUserManager->delete()
 	 */
 	public static function deleteUser($uid) {
 		$user = self::getManager()->get($uid);
 		if ($user) {
-			$result = $user->delete();
-
-			// if delete was successful we clean-up the rest
-			if ($result) {
-
-				// We have to delete the user from all groups
-				foreach (OC_Group::getUserGroups($uid) as $i) {
-					OC_Group::removeFromGroup($uid, $i);
-				}
-				// Delete the user's keys in preferences
-				OC_Preferences::deleteUser($uid);
-
-				// Delete user files in /data/
-				OC_Helper::rmdirr(\OC_User::getHome($uid));
-
-				// Delete the users entry in the storage table
-				\OC\Files\Cache\Storage::remove('home::' . $uid);
-			}
-
-			return true;
+			return $user->delete();
 		} else {
 			return false;
 		}
@@ -525,6 +509,7 @@ class OC_User {
 	 * @return string
 	 *
 	 * returns the path to the users home directory
+	 * @deprecated Use \OC::$server->getUserManager->getHome()
 	 */
 	public static function getHome($uid) {
 		$user = self::getManager()->get($uid);
diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php
index 0c01f957bd30b4efe82a6561b146d673ac3cadf4..2403f45aa2f4a6f5ebe019b46323975ddafc3864 100644
--- a/lib/private/user/manager.php
+++ b/lib/private/user/manager.php
@@ -220,7 +220,7 @@ class Manager extends PublicEmitter implements IUserManager {
 	 * @param string $uid
 	 * @param string $password
 	 * @throws \Exception
-	 * @return bool|\OC\User\User the created user of false
+	 * @return bool|\OC\User\User the created user or false
 	 */
 	public function createUser($uid, $password) {
 		$l = \OC::$server->getL10N('lib');
diff --git a/lib/private/user/user.php b/lib/private/user/user.php
index 9ad2f5f0d3a68d9511fa1aa5bc031d0f0a3d1370..ad85337f628368864b7514669ff47a8f1f9e61c3 100644
--- a/lib/private/user/user.php
+++ b/lib/private/user/user.php
@@ -153,6 +153,24 @@ class User implements IUser {
 			$this->emitter->emit('\OC\User', 'preDelete', array($this));
 		}
 		$result = $this->backend->deleteUser($this->uid);
+		if ($result) {
+
+			// FIXME: Feels like an hack - suggestions?
+
+			// We have to delete the user from all groups
+			foreach (\OC_Group::getUserGroups($this->uid) as $i) {
+				\OC_Group::removeFromGroup($this->uid, $i);
+			}
+			// Delete the user's keys in preferences
+			\OC_Preferences::deleteUser($this->uid);
+
+			// Delete user files in /data/
+			\OC_Helper::rmdirr(\OC_User::getHome($this->uid));
+
+			// Delete the users entry in the storage table
+			\OC\Files\Cache\Storage::remove('home::' . $this->uid);
+		}
+
 		if ($this->emitter) {
 			$this->emitter->emit('\OC\User', 'postDelete', array($this));
 		}
diff --git a/settings/ajax/creategroup.php b/settings/ajax/creategroup.php
deleted file mode 100644
index be376bea9dc18655bcc9c37a5a824e3257c3d48d..0000000000000000000000000000000000000000
--- a/settings/ajax/creategroup.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-OCP\JSON::callCheck();
-OC_JSON::checkAdminUser();
-
-$groupname = $_POST["groupname"];
-$l = \OC::$server->getL10N('settings');
-
-// Does the group exist?
-if( in_array( $groupname, OC_Group::getGroups())) {
-	OC_JSON::error(array("data" => array( "message" => $l->t("Group already exists") )));
-	exit();
-}
-
-// Return Success story
-if( OC_Group::createGroup( $groupname )) {
-	OC_JSON::success(array("data" => array( "groupname" => $groupname )));
-}
-else{
-	OC_JSON::error(array("data" => array( "message" => $l->t("Unable to add group") )));
-}
diff --git a/settings/ajax/createuser.php b/settings/ajax/createuser.php
deleted file mode 100644
index 463c15d59e85d21c7ad72d6b53080bffde77a52a..0000000000000000000000000000000000000000
--- a/settings/ajax/createuser.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-OCP\JSON::callCheck();
-OC_JSON::checkSubAdminUser();
-
-if(OC_User::isAdminUser(OC_User::getUser())) {
-	$groups = array();
-	if (!empty($_POST["groups"])) {
-		$groups = $_POST["groups"];
-	}
-}else{
-	if (isset($_POST["groups"])) {
-		$groups = array();
-		if (!empty($_POST["groups"])) {
-			foreach ($_POST["groups"] as $group) {
-				if (OC_SubAdmin::isGroupAccessible(OC_User::getUser(), $group)) {
-					$groups[] = $group;
-				}
-			}
-		}
-		if (empty($groups)) {
-			$groups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser());
-		}
-	} else {
-		$groups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser());
-	}
-}
-$username = $_POST["username"];
-$password = $_POST["password"];
-
-// Return Success story
-try {
-	// check whether the user's files home exists
-	$userDirectory = OC_User::getHome($username) . '/files/';
-	$homeExists = file_exists($userDirectory);
-
-	if (!OC_User::createUser($username, $password)) {
-		OC_JSON::error(array('data' => array( 'message' => 'User creation failed for '.$username )));
-		exit();
-	}
-	foreach( $groups as $i ) {
-		if(!OC_Group::groupExists($i)) {
-			OC_Group::createGroup($i);
-		}
-		OC_Group::addToGroup( $username, $i );
-	}
-
-	$userManager = \OC_User::getManager();
-	$user = $userManager->get($username);
-	OCP\JSON::success(array("data" =>
-				array(
-					// returns whether the home already existed
-					"homeExists" => $homeExists,
-					"username" => $username,
-					"groups" => OC_Group::getUserGroups( $username ),
-					'storageLocation' => $user->getHome())));
-} catch (Exception $exception) {
-	OCP\JSON::error(array("data" => array( "message" => $exception->getMessage())));
-}
diff --git a/settings/ajax/grouplist.php b/settings/ajax/grouplist.php
deleted file mode 100644
index 93bb510773da56beddcfeb72749a6d556d7634dd..0000000000000000000000000000000000000000
--- a/settings/ajax/grouplist.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-/**
- * ownCloud
- *
- * @author Arthur Schiwon
- * @copyright 2014 Arthur Schiwon <blizzz@owncloud.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-OC_JSON::callCheck();
-OC_JSON::checkSubAdminUser();
-if (isset($_GET['pattern']) && !empty($_GET['pattern'])) {
-	$pattern = $_GET['pattern'];
-} else {
-	$pattern = '';
-}
-if (isset($_GET['filterGroups']) && !empty($_GET['filterGroups'])) {
-	$filterGroups = intval($_GET['filterGroups']) === 1;
-} else {
-	$filterGroups = false;
-}
-$groupPattern = $filterGroups ? $pattern : '';
-$groups = array();
-$adminGroups = array();
-$groupManager = \OC_Group::getManager();
-$isAdmin = OC_User::isAdminUser(OC_User::getUser());
-
-$groupsInfo = new \OC\Group\MetaData(OC_User::getUser(), $isAdmin, $groupManager);
-$groupsInfo->setSorting($groupsInfo::SORT_USERCOUNT);
-list($adminGroups, $groups) = $groupsInfo->get($groupPattern, $pattern);
-
-OC_JSON::success(
-	array('data' => array('adminGroups' => $adminGroups, 'groups' => $groups)));
diff --git a/settings/ajax/removegroup.php b/settings/ajax/removegroup.php
deleted file mode 100644
index 798d7916e61f2de452e0abf90ebcd6354df28a9f..0000000000000000000000000000000000000000
--- a/settings/ajax/removegroup.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-OC_JSON::checkAdminUser();
-OCP\JSON::callCheck();
-
-$name = $_POST["groupname"];
-
-// Return Success story
-if( OC_Group::deleteGroup( $name )) {
-	OC_JSON::success(array("data" => array( "groupname" => $name )));
-}
-else{
-	OC_JSON::error(array("data" => array( "message" => $l->t("Unable to delete group") )));
-}
diff --git a/settings/ajax/removeuser.php b/settings/ajax/removeuser.php
deleted file mode 100644
index eda852387801b73b96c9f6525c607db6f26be99d..0000000000000000000000000000000000000000
--- a/settings/ajax/removeuser.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-OC_JSON::checkSubAdminUser();
-OCP\JSON::callCheck();
-
-$username = $_POST["username"];
-
-// A user shouldn't be able to delete his own account
-if(OC_User::getUser() === $username) {
-	exit;
-}
-
-if(!OC_User::isAdminUser(OC_User::getUser()) && !OC_SubAdmin::isUserAccessible(OC_User::getUser(), $username)) {
-	$l = \OC::$server->getL10N('core');
-	OC_JSON::error(array( 'data' => array( 'message' => $l->t('Authentication error') )));
-	exit();
-}
-
-// Return Success story
-if( OC_User::deleteUser( $username )) {
-	OC_JSON::success(array("data" => array( "username" => $username )));
-}
-else{
-	$l = \OC::$server->getL10N('core');
-	OC_JSON::error(array("data" => array( "message" => $l->t("Unable to delete user") )));
-}
diff --git a/settings/ajax/userlist.php b/settings/ajax/userlist.php
deleted file mode 100644
index 807cf5f1899610ad74492e6a8161ee48534d168d..0000000000000000000000000000000000000000
--- a/settings/ajax/userlist.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-/**
- * ownCloud
- *
- * @author Michael Gapczynski
- * @copyright 2012 Michael Gapczynski mtgap@owncloud.com
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-OC_JSON::callCheck();
-OC_JSON::checkSubAdminUser();
-if (isset($_GET['offset'])) {
-	$offset = $_GET['offset'];
-} else {
-	$offset = 0;
-}
-if (isset($_GET['limit'])) {
-	$limit = $_GET['limit'];
-} else {
-	$limit = 10;
-}
-if (isset($_GET['gid']) && !empty($_GET['gid'])) {
-	$gid = $_GET['gid'];
-	if ($gid === '_everyone') {
-		$gid = false;
-	}
-} else {
-	$gid = false;
-}
-if (isset($_GET['pattern']) && !empty($_GET['pattern'])) {
-	$pattern = $_GET['pattern'];
-} else {
-	$pattern = '';
-}
-$users = array();
-$userManager = \OC_User::getManager();
-if (OC_User::isAdminUser(OC_User::getUser())) {
-	if($gid !== false) {
-		$batch = OC_Group::displayNamesInGroup($gid, $pattern, $limit, $offset);
-	} else {
-		$batch = OC_User::getDisplayNames($pattern, $limit, $offset);
-	}
-	foreach ($batch as $uid => $displayname) {
-		$user = $userManager->get($uid);
-		$users[] = array(
-			'name' => $uid,
-			'displayname' => $displayname,
-			'groups' => OC_Group::getUserGroups($uid),
-			'subadmin' => OC_SubAdmin::getSubAdminsGroups($uid),
-			'quota' => OC_Preferences::getValue($uid, 'files', 'quota', 'default'),
-			'storageLocation' => $user->getHome(),
-			'lastLogin' => $user->getLastLogin(),
-		);
-	}
-} else {
-	$groups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser());
-	if($gid !== false && in_array($gid, $groups)) {
-		$groups = array($gid);
-	} elseif($gid !== false) {
-		//don't you try to investigate loops you must not know about
-		$groups = array();
-	}
-	$batch = OC_Group::usersInGroups($groups, $pattern, $limit, $offset);
-	foreach ($batch as $uid) {
-		$user = $userManager->get($uid);
-
-		// Only add the groups, this user is a subadmin of
-		$userGroups = array_intersect(OC_Group::getUserGroups($uid), OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()));
-		$users[] = array(
-			'name' => $uid,
-			'displayname' => $user->getDisplayName(),
-			'groups' => $userGroups,
-			'quota' => OC_Preferences::getValue($uid, 'files', 'quota', 'default'),
-			'storageLocation' => $user->getHome(),
-			'lastLogin' => $user->getLastLogin(),
-		);
-	}
-}
-OC_JSON::success(array('data' => $users));
diff --git a/settings/application.php b/settings/application.php
index 64aa4671228dc4061afbab8222a1949d7bba6460..0a80bd8b1e71d68642b985359f8b13c7aa17847e 100644
--- a/settings/application.php
+++ b/settings/application.php
@@ -10,11 +10,14 @@
 
 namespace OC\Settings;
 
-use OC\AppFramework\Utility\SimpleContainer;
 use OC\Settings\Controller\AppSettingsController;
+use OC\Settings\Controller\GroupsController;
 use OC\Settings\Controller\MailSettingsController;
 use OC\Settings\Controller\SecuritySettingsController;
+use OC\Settings\Controller\UsersController;
+use OC\Settings\Middleware\SubadminMiddleware;
 use \OCP\AppFramework\App;
+use OCP\IContainer;
 use \OCP\Util;
 
 /**
@@ -34,7 +37,7 @@ class Application extends App {
 		/**
 		 * Controllers
 		 */
-		$container->registerService('MailSettingsController', function(SimpleContainer $c) {
+		$container->registerService('MailSettingsController', function(IContainer $c) {
 			return new MailSettingsController(
 				$c->query('AppName'),
 				$c->query('Request'),
@@ -46,7 +49,7 @@ class Application extends App {
 				$c->query('DefaultMailAddress')
 			);
 		});
-		$container->registerService('AppSettingsController', function(SimpleContainer $c) {
+		$container->registerService('AppSettingsController', function(IContainer $c) {
 			return new AppSettingsController(
 				$c->query('AppName'),
 				$c->query('Request'),
@@ -54,33 +57,81 @@ class Application extends App {
 				$c->query('Config')
 			);
 		});
-		$container->registerService('SecuritySettingsController', function(SimpleContainer $c) {
+		$container->registerService('SecuritySettingsController', function(IContainer $c) {
 			return new SecuritySettingsController(
 				$c->query('AppName'),
 				$c->query('Request'),
 				$c->query('Config')
 			);
 		});
+		$container->registerService('GroupsController', function(IContainer $c) {
+			return new GroupsController(
+				$c->query('AppName'),
+				$c->query('Request'),
+				$c->query('GroupManager'),
+				$c->query('UserSession'),
+				$c->query('IsAdmin'),
+				$c->query('L10N')
+			);
+		});
+		$container->registerService('UsersController', function(IContainer $c) {
+			return new UsersController(
+				$c->query('AppName'),
+				$c->query('Request'),
+				$c->query('UserManager'),
+				$c->query('GroupManager'),
+				$c->query('UserSession'),
+				$c->query('Config'),
+				$c->query('IsAdmin'),
+				$c->query('L10N')
+			);
+		});
+
+		/**
+		 * Middleware
+		 */
+		$container->registerService('SubadminMiddleware', function(IContainer $c){
+			return new SubadminMiddleware(
+				$c->query('ControllerMethodReflector'),
+				$c->query('IsSubAdmin')
+			);
+		});
+		// Execute middlewares
+		$container->registerMiddleware('SubadminMiddleware');
 
 		/**
 		 * Core class wrappers
 		 */
-		$container->registerService('Config', function(SimpleContainer $c) {
+		$container->registerService('Config', function(IContainer $c) {
 			return $c->query('ServerContainer')->getConfig();
 		});
-		$container->registerService('L10N', function(SimpleContainer $c) {
+		$container->registerService('L10N', function(IContainer $c) {
 			return $c->query('ServerContainer')->getL10N('settings');
 		});
-		$container->registerService('UserSession', function(SimpleContainer $c) {
+		$container->registerService('GroupManager', function(IContainer $c) {
+			return $c->query('ServerContainer')->getGroupManager();
+		});
+		$container->registerService('UserManager', function(IContainer $c) {
+			return $c->query('ServerContainer')->getUserManager();
+		});
+		$container->registerService('UserSession', function(IContainer $c) {
 			return $c->query('ServerContainer')->getUserSession();
 		});
-		$container->registerService('Mail', function(SimpleContainer $c) {
+		/** FIXME: Remove once OC_User is non-static and mockable */
+		$container->registerService('IsAdmin', function(IContainer $c) {
+			return \OC_User::isAdminUser(\OC_User::getUser());
+		});
+		/** FIXME: Remove once OC_SubAdmin is non-static and mockable */
+		$container->registerService('IsSubAdmin', function(IContainer $c) {
+			return \OC_Subadmin::isSubAdmin(\OC_User::getUser());
+		});
+		$container->registerService('Mail', function(IContainer $c) {
 			return new \OC_Mail;
 		});
-		$container->registerService('Defaults', function(SimpleContainer $c) {
+		$container->registerService('Defaults', function(IContainer $c) {
 			return new \OC_Defaults;
 		});
-		$container->registerService('DefaultMailAddress', function(SimpleContainer $c) {
+		$container->registerService('DefaultMailAddress', function(IContainer $c) {
 			return Util::getDefaultEmailAddress('no-reply');
 		});
 	}
diff --git a/settings/controller/groupscontroller.php b/settings/controller/groupscontroller.php
new file mode 100644
index 0000000000000000000000000000000000000000..82e72821c3d19c36c3c20ed2e792cf88e41c0dc8
--- /dev/null
+++ b/settings/controller/groupscontroller.php
@@ -0,0 +1,140 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Settings\Controller;
+
+use OC\AppFramework\Http;
+use \OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IGroupManager;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IUserSession;
+
+/**
+ * @package OC\Settings\Controller
+ */
+class GroupsController extends Controller {
+	/** @var IGroupManager */
+	private $groupManager;
+	/** @var IL10N */
+	private $l10n;
+	/** @var IUserSession */
+	private $userSession;
+	/** @var bool */
+	private $isAdmin;
+
+	/**
+	 * @param string $appName
+	 * @param IRequest $request
+	 * @param IGroupManager $groupManager
+	 * @param IUserSession $userSession
+	 * @param bool $isAdmin
+	 * @param IL10N $l10n
+	 */
+	public function __construct($appName,
+								IRequest $request,
+								IGroupManager $groupManager,
+								IUserSession $userSession,
+								$isAdmin,
+								IL10N $l10n) {
+		parent::__construct($appName, $request);
+		$this->groupManager = $groupManager;
+		$this->userSession = $userSession;
+		$this->isAdmin = $isAdmin;
+		$this->l10n = $l10n;
+	}
+
+	/**
+	 * @NoAdminRequired
+	 *
+	 * @param string $pattern
+	 * @param bool $filterGroups
+	 * @return DataResponse
+	 */
+	public function index($pattern = '', $filterGroups = false) {
+		$groupPattern = $filterGroups ? $pattern : '';
+
+		$groupsInfo = new \OC\Group\MetaData($this->userSession->getUser()->getUID(),
+			$this->isAdmin, $this->groupManager);
+		$groupsInfo->setSorting($groupsInfo::SORT_USERCOUNT);
+		list($adminGroups, $groups) = $groupsInfo->get($groupPattern, $pattern);
+
+		return new DataResponse(
+			array(
+				'data' => array('adminGroups' => $adminGroups, 'groups' => $groups)
+			)
+		);
+	}
+
+	/**
+	 * @param string $id
+	 * @return DataResponse
+	 */
+	public function create($id) {
+		if($this->groupManager->groupExists($id)) {
+			return new DataResponse(
+				array(
+					'message' => (string)$this->l10n->t('Group already exists.')
+				),
+				Http::STATUS_CONFLICT
+			);
+		}
+		if($this->groupManager->createGroup($id)) {
+			return new DataResponse(
+				array(
+					'groupname' => $id
+				),
+				Http::STATUS_CREATED
+			);
+		}
+
+		return new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array(
+					'message' => (string)$this->l10n->t('Unable to add group.')
+				)
+			),
+			Http::STATUS_FORBIDDEN
+		);
+	}
+
+	/**
+	 * @param string $id
+	 * @return DataResponse
+	 */
+	public function destroy($id) {
+		$group = $this->groupManager->get($id);
+		if ($group) {
+			if ($group->delete()) {
+				return new DataResponse(
+					array(
+						'status' => 'success',
+						'data' => array(
+							'groupname' => $id
+						)
+					),
+					Http::STATUS_NO_CONTENT
+				);
+			}
+		}
+		return new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array(
+					'message' => (string)$this->l10n->t('Unable to delete group.')
+				),
+			),
+			Http::STATUS_FORBIDDEN
+		);
+	}
+
+}
diff --git a/settings/controller/userscontroller.php b/settings/controller/userscontroller.php
new file mode 100644
index 0000000000000000000000000000000000000000..5bd4b5551068be9cad176c325f329a4118d50b25
--- /dev/null
+++ b/settings/controller/userscontroller.php
@@ -0,0 +1,253 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Settings\Controller;
+
+use OC\AppFramework\Http;
+use OC\User\User;
+use \OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IUserManager;
+use OCP\IUserSession;
+
+/**
+ * @package OC\Settings\Controller
+ */
+class UsersController extends Controller {
+	/** @var IL10N */
+	private $l10n;
+	/** @var IUserSession */
+	private $userSession;
+	/** @var bool */
+	private $isAdmin;
+	/** @var IUserManager */
+	private $userManager;
+	/** @var IGroupManager */
+	private $groupManager;
+	/** @var IConfig */
+	private $config;
+
+	/**
+	 * @param string $appName
+	 * @param IRequest $request
+	 * @param IUserManager $userManager
+	 * @param IGroupManager $groupManager
+	 * @param IUserSession $userSession
+	 * @param IConfig $config
+	 * @param bool $isAdmin
+	 * @param IL10N $l10n
+	 */
+	public function __construct($appName,
+								IRequest $request,
+								IUserManager $userManager,
+								IGroupManager $groupManager,
+								IUserSession $userSession,
+								IConfig $config,
+								$isAdmin,
+								IL10N $l10n) {
+		parent::__construct($appName, $request);
+		$this->userManager = $userManager;
+		$this->groupManager = $groupManager;
+		$this->userSession = $userSession;
+		$this->config = $config;
+		$this->isAdmin = $isAdmin;
+		$this->l10n = $l10n;
+	}
+
+	/**
+	 * @NoAdminRequired
+	 * @NoCSRFRequired
+	 * @param int $offset
+	 * @param int $limit
+	 * @param string $gid
+	 * @param string $pattern
+	 * @return DataResponse
+	 *
+	 * TODO: Tidy up and write unit tests - code is mainly static method calls
+	 */
+	public function index($offset = 0, $limit = 10, $gid = '', $pattern = '') {
+		// FIXME: The JS sends the group '_everyone' instead of no GID for the "all users" group.
+		if($gid === '_everyone') {
+			$gid = '';
+		}
+		$users = array();
+		if ($this->isAdmin) {
+			if($gid !== '') {
+				$batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset);
+			} else {
+				// FIXME: Remove static method call
+				$batch = \OC_User::getDisplayNames($pattern, $limit, $offset);
+			}
+
+			foreach ($batch as $uid => $displayname) {
+				$user = $this->userManager->get($uid);
+				$users[] = array(
+					'name' => $uid,
+					'displayname' => $displayname,
+					'groups' => $this->groupManager->getUserGroupIds($user),
+					'subadmin' => \OC_SubAdmin::getSubAdminsGroups($uid),
+					'quota' => $this->config->getUserValue($uid, 'files', 'quota', 'default'),
+					'storageLocation' => $user->getHome(),
+					'lastLogin' => $user->getLastLogin(),
+				);
+			}
+		} else {
+			$groups = \OC_SubAdmin::getSubAdminsGroups($this->userSession->getUser()->getUID());
+			if($gid !== '' && in_array($gid, $groups)) {
+				$groups = array($gid);
+			} elseif($gid !== '') {
+				//don't you try to investigate loops you must not know about
+				$groups = array();
+			}
+			$batch = \OC_Group::usersInGroups($groups, $pattern, $limit, $offset);
+			foreach ($batch as $uid) {
+				$user = $this->userManager->get($uid);
+
+				// Only add the groups, this user is a subadmin of
+				$userGroups = array_intersect($this->groupManager->getUserGroupIds($user), \OC_SubAdmin::getSubAdminsGroups($this->userSession->getUser()->getUID()));
+				$users[] = array(
+					'name' => $uid,
+					'displayname' => $user->getDisplayName(),
+					'groups' => $userGroups,
+					'quota' => $this->config->getUserValue($uid, 'files', 'quota', 'default'),
+					'storageLocation' => $user->getHome(),
+					'lastLogin' => $user->getLastLogin(),
+				);
+			}
+		}
+
+		// FIXME: That assignment on "data" is uneeded here - JS should be adjusted
+		return new DataResponse(array('data' => $users, 'status' => 'success'));
+	}
+
+	/**
+	 * @NoAdminRequired
+	 *
+	 * @param string $username
+	 * @param string $password
+	 * @param array $groups
+	 * @return DataResponse
+	 *
+	 * TODO: Tidy up and write unit tests - code is mainly static method calls
+	 */
+	public function create($username, $password, array $groups) {
+		
+		if (!$this->isAdmin) {
+			if (!empty($groups)) {
+				foreach ($groups as $key => $group) {
+					if (!\OC_SubAdmin::isGroupAccessible($this->userSession->getUser()->getUID(), $group)) {
+						unset($groups[$key]);
+					}
+				}
+			}
+			if (empty($groups)) {
+				$groups = \OC_SubAdmin::getSubAdminsGroups($this->userSession->getUser()->getUID());
+			}
+		}
+
+		try {
+			$user = $this->userManager->createUser($username, $password);
+		} catch (\Exception $exception) {
+			return new DataResponse(
+				array(
+					'message' => (string)$this->l10n->t('Unable to create user.')
+				),
+				Http::STATUS_FORBIDDEN
+			);
+		}
+
+		if($user instanceof User) {
+			foreach( $groups as $groupName ) {
+				$group = $this->groupManager->get($groupName);
+
+				if(empty($group)) {
+					$group = $this->groupManager->createGroup($groupName);
+				}
+				$group->addUser($user);
+			}
+		}
+
+		return new DataResponse(
+			array(
+				'username' => $username,
+				'groups' => $this->groupManager->getUserGroupIds($user),
+				'storageLocation' => $user->getHome()
+			),
+			Http::STATUS_CREATED
+		);
+
+	}
+
+	/**
+	 * @NoAdminRequired
+	 *
+	 * @param string $id
+	 * @return DataResponse
+	 *
+	 * TODO: Tidy up and write unit tests - code is mainly static method calls
+	 */
+	public function destroy($id) {
+		if($this->userSession->getUser()->getUID() === $id) {
+			return new DataResponse(
+				array(
+					'status' => 'error',
+					'data' => array(
+						'message' => (string)$this->l10n->t('Unable to delete user.')
+					)
+				),
+				Http::STATUS_FORBIDDEN
+			);
+		}
+
+		// FIXME: Remove this static function call at some point…
+		if(!$this->isAdmin && !\OC_SubAdmin::isUserAccessible($this->userSession->getUser()->getUID(), $id)) {
+			return new DataResponse(
+				array(
+					'status' => 'error',
+					'data' => array(
+						'message' => (string)$this->l10n->t('Authentication error')
+					)
+				),
+				Http::STATUS_FORBIDDEN
+			);
+		}
+
+		$user = $this->userManager->get($id);
+		if($user) {
+			if($user->delete()) {
+				return new DataResponse(
+					array(
+						'status' => 'success',
+						'data' => array(
+							'username' => $id
+						)
+					),
+					Http::STATUS_NO_CONTENT
+				);
+			}
+		}
+
+		return new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array(
+					'message' => (string)$this->l10n->t('Unable to delete user.')
+				)
+			),
+			Http::STATUS_FORBIDDEN
+		);
+
+	}
+
+}
diff --git a/settings/js/settings.js b/settings/js/settings.js
index 13c56a8f53aa1c4de3afbb1d10abb3ec8fdef1bd..e98bd2cc89567af5f7d3d49b659cd527779ae864 100644
--- a/settings/js/settings.js
+++ b/settings/js/settings.js
@@ -41,7 +41,7 @@ OC.Settings = _.extend(OC.Settings, {
 						};
 					}
 					$.ajax({
-						url: OC.generateUrl('/settings/ajax/grouplist'),
+						url: OC.generateUrl('/settings/users/groups'),
 						data: queryData,
 						dataType: 'json',
 						success: function(data) {
diff --git a/settings/js/users/deleteHandler.js b/settings/js/users/deleteHandler.js
index c89a844044e69d78507eb70bf8fdcc895959575b..942bae91cd36bb6b771175ea108543873d4c6291 100644
--- a/settings/js/users/deleteHandler.js
+++ b/settings/js/users/deleteHandler.js
@@ -189,11 +189,10 @@ DeleteHandler.prototype.deleteEntry = function(keepNotification) {
 	var payload = {};
 	payload[dh.ajaxParamID] = dh.oidToDelete;
 	$.ajax({
-		type: 'POST',
-		url: OC.filePath('settings', 'ajax', dh.ajaxEndpoint),
+		type: 'DELETE',
+		url: OC.generateUrl(dh.ajaxEndpoint+'/'+this.oidToDelete),
 		// FIXME: do not use synchronous ajax calls as they block the browser !
 		async: false,
-		data: payload,
 		success: function (result) {
 			if (result.status === 'success') {
 				// Remove undo option, & remove user from table
diff --git a/settings/js/users/groups.js b/settings/js/users/groups.js
index 081842734f06d7d0255f155a20924acafaa27e04..c06bc5ff14be8dc035087bb664ceb118e479d530 100644
--- a/settings/js/users/groups.js
+++ b/settings/js/users/groups.js
@@ -84,29 +84,24 @@ GroupList = {
 
 	createGroup: function (groupname) {
 		$.post(
-			OC.filePath('settings', 'ajax', 'creategroup.php'),
+			OC.generateUrl('/settings/users/groups'),
 			{
-				groupname: groupname
+				id: groupname
 			},
 			function (result) {
-				if (result.status !== 'success') {
-					OC.dialogs.alert(result.data.message,
-						t('settings', 'Error creating group'));
+				if (result.groupname) {
+					var addedGroup = result.groupname;
+					UserList.availableGroups = $.unique($.merge(UserList.availableGroups, [addedGroup]));
+					GroupList.addGroup(result.groupname);
+
+					$('.groupsselect, .subadminsselect')
+						.append($('<option>', { value: result.groupname })
+							.text(result.groupname));
 				}
-				else {
-					if (result.data.groupname) {
-						var addedGroup = result.data.groupname;
-						UserList.availableGroups = $.unique($.merge(UserList.availableGroups, [addedGroup]));
-						GroupList.addGroup(result.data.groupname);
-
-						$('.groupsselect, .subadminsselect')
-							.append($('<option>', { value: result.data.groupname })
-								.text(result.data.groupname));
-					}
-					GroupList.toggleAddGroup();
-				}
-			}
-		);
+				GroupList.toggleAddGroup();
+			}).fail(function(result, textStatus, errorThrown) {
+				OC.dialogs.alert(result.responseJSON.message, t('settings', 'Error creating group'));
+			});
 	},
 
 	update: function () {
@@ -115,7 +110,7 @@ GroupList = {
 		}
 		GroupList.updating = true;
 		$.get(
-			OC.generateUrl('/settings/ajax/grouplist'),
+			OC.generateUrl('/settings/users/groups'),
 			{
 				pattern: filter.getPattern(),
 				filterGroups: filter.filterGroups ? 1 : 0
@@ -221,7 +216,7 @@ GroupList = {
 	},
 	initDeleteHandling: function () {
 		//set up handler
-		GroupDeleteHandler = new DeleteHandler('removegroup.php', 'groupname',
+		GroupDeleteHandler = new DeleteHandler('/settings/users/groups', 'groupname',
 			GroupList.hide, GroupList.remove);
 
 		//configure undo
diff --git a/settings/js/users/users.js b/settings/js/users/users.js
index 5e0c0cac189da3980855c0920f7cafdc8166076d..1bca6d06b33719230c4c03af59754b72c0584d4f 100644
--- a/settings/js/users/users.js
+++ b/settings/js/users/users.js
@@ -292,7 +292,7 @@ var UserList = {
 	},
 	initDeleteHandling: function() {
 		//set up handler
-		UserDeleteHandler = new DeleteHandler('removeuser.php', 'username',
+		UserDeleteHandler = new DeleteHandler('/settings/users/users', 'username',
 											UserList.markRemove, UserList.remove);
 
 		//configure undo
@@ -326,7 +326,7 @@ var UserList = {
 		UserList.currentGid = gid;
 		var pattern = filter.getPattern();
 		$.get(
-			OC.generateUrl('/settings/ajax/userlist'),
+			OC.generateUrl('/settings/users/users'),
 			{ offset: UserList.offset, limit: UserList.usersToLoad, gid: gid, pattern: pattern },
 			function (result) {
 				var loadedUsers = 0;
@@ -667,49 +667,44 @@ $(document).ready(function () {
 		var groups = $('#newusergroups').val();
 		$('#newuser').get(0).reset();
 		$.post(
-			OC.filePath('settings', 'ajax', 'createuser.php'),
+			OC.generateUrl('/settings/users/users'),
 			{
 				username: username,
 				password: password,
 				groups: groups
 			},
 			function (result) {
-				if (result.status !== 'success') {
-					OC.dialogs.alert(result.data.message,
-						t('settings', 'Error creating user'));
-				} else {
-					if (result.data.groups) {
-						var addedGroups = result.data.groups;
-						for (var i in result.data.groups) {
-							var gid = result.data.groups[i];
-							if(UserList.availableGroups.indexOf(gid) === -1) {
-								UserList.availableGroups.push(gid);
-							}
-							$li = GroupList.getGroupLI(gid);
-							userCount = GroupList.getUserCount($li);
-							GroupList.setUserCount($li, userCount + 1);
+				if (result.groups) {
+					for (var i in result.groups) {
+						var gid = result.groups[i];
+						if(UserList.availableGroups.indexOf(gid) === -1) {
+							UserList.availableGroups.push(gid);
 						}
+						$li = GroupList.getGroupLI(gid);
+						userCount = GroupList.getUserCount($li);
+						GroupList.setUserCount($li, userCount + 1);
 					}
-					if (result.data.homeExists){
-						OC.Notification.hide();
-						OC.Notification.show(t('settings', 'Warning: Home directory for user "{user}" already exists', {user: result.data.username}));
-						if (UserList.notificationTimeout){
-							window.clearTimeout(UserList.notificationTimeout);
-						}
-						UserList.notificationTimeout = window.setTimeout(
-							function(){
-								OC.Notification.hide();
-								UserList.notificationTimeout = null;
-							}, 10000);
-					}
-					if(!UserList.has(username)) {
-						UserList.add(username, username, result.data.groups, null, 'default', result.data.storageLocation, 0, true);
+				}
+				if (result.homeExists){
+					OC.Notification.hide();
+					OC.Notification.show(t('settings', 'Warning: Home directory for user "{user}" already exists', {user: result.username}));
+					if (UserList.notificationTimeout){
+						window.clearTimeout(UserList.notificationTimeout);
 					}
-					$('#newusername').focus();
-					GroupList.incEveryoneCount();
+					UserList.notificationTimeout = window.setTimeout(
+						function(){
+							OC.Notification.hide();
+							UserList.notificationTimeout = null;
+						}, 10000);
 				}
-			}
-		);
+				if(!UserList.has(username)) {
+					UserList.add(username, username, result.groups, null, 'default', result.storageLocation, 0, true);
+				}
+				$('#newusername').focus();
+				GroupList.incEveryoneCount();
+			}).fail(function(result, textStatus, errorThrown) {
+				OC.dialogs.alert(result.responseJSON.message, t('settings', 'Error creating user'));
+			});
 	});
 
 	// Option to display/hide the "Storage location" column
diff --git a/settings/middleware/subadminmiddleware.php b/settings/middleware/subadminmiddleware.php
new file mode 100644
index 0000000000000000000000000000000000000000..a5c005e3148d40b0a25ec694dca352d063859ff6
--- /dev/null
+++ b/settings/middleware/subadminmiddleware.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Settings\Middleware;
+
+use OC\AppFramework\Http;
+use OC\AppFramework\Utility\ControllerMethodReflector;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Middleware;
+
+/**
+ * Verifies whether an user has at least subadmin rights.
+ * To bypass use the `@NoSubadminRequired` annotation
+ *
+ * @package OC\Settings\Middleware
+ */
+class SubadminMiddleware extends Middleware {
+	/** @var bool */
+	protected $isSubAdmin;
+	/** @var ControllerMethodReflector */
+	protected $reflector;
+
+	/**
+	 * @param ControllerMethodReflector $reflector
+	 * @param bool $isSubAdmin
+	 */
+	public function __construct(ControllerMethodReflector $reflector,
+								$isSubAdmin) {
+		$this->reflector = $reflector;
+		$this->isSubAdmin = $isSubAdmin;
+	}
+
+	/**
+	 * Check if sharing is enabled before the controllers is executed
+	 * @param \OCP\AppFramework\Controller $controller
+	 * @param string $methodName
+	 * @throws \Exception
+	 */
+	public function beforeController($controller, $methodName) {
+		if(!$this->reflector->hasAnnotation('NoSubadminRequired')) {
+			if(!$this->isSubAdmin) {
+				throw new \Exception('Logged in user must be a subadmin');
+			}
+		}
+	}
+
+	/**
+	 * Return 403 page in case of an exception
+	 * @param \OCP\AppFramework\Controller $controller
+	 * @param string $methodName
+	 * @param \Exception $exception
+	 * @return TemplateResponse
+	 */
+	public function afterException($controller, $methodName, \Exception $exception) {
+		return new TemplateResponse('core', '403', array(), 'guest');
+	}
+
+}
diff --git a/settings/routes.php b/settings/routes.php
index 7ca33fc27459a29b2d88bfee283e8a77bc44a12f..1b7a918fa799e591ac5146b435ccf884fcefd737 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -9,17 +9,22 @@
 namespace OC\Settings;
 
 $application = new Application();
-$application->registerRoutes($this, array('routes' =>array(
-	array('name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'),
-	array('name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'),
-	array('name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'),
-	array('name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'),
-	array('name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'),
-	array('name' => 'SecuritySettings#enforceSSL', 'url' => '/settings/admin/security/ssl', 'verb' => 'POST'),
-	array('name' => 'SecuritySettings#enforceSSLForSubdomains', 'url' => '/settings/admin/security/ssl/subdomains', 'verb' => 'POST'),
-	array('name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'),
-
-)));
+$application->registerRoutes($this, array(
+	'resources' => array(
+		'groups' => array('url' => '/settings/users/groups'),
+		'users' => array('url' => '/settings/users/users')
+	),
+	'routes' =>array(
+		array('name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'),
+		array('name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'),
+		array('name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'),
+		array('name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'),
+		array('name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'),
+		array('name' => 'SecuritySettings#enforceSSL', 'url' => '/settings/admin/security/ssl', 'verb' => 'POST'),
+		array('name' => 'SecuritySettings#enforceSSLForSubdomains', 'url' => '/settings/admin/security/ssl/subdomains', 'verb' => 'POST'),
+		array('name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'),
+	)
+));
 
 /** @var $this \OCP\Route\IRouter */
 
@@ -38,26 +43,14 @@ $this->create('settings_admin', '/settings/admin')
 	->actionInclude('settings/admin.php');
 // Settings ajax actions
 // users
-$this->create('settings_ajax_userlist', '/settings/ajax/userlist')
-	->actionInclude('settings/ajax/userlist.php');
-$this->create('settings_ajax_grouplist', '/settings/ajax/grouplist')
-	->actionInclude('settings/ajax/grouplist.php');
 $this->create('settings_ajax_everyonecount', '/settings/ajax/geteveryonecount')
 	->actionInclude('settings/ajax/geteveryonecount.php');
-$this->create('settings_ajax_createuser', '/settings/ajax/createuser.php')
-	->actionInclude('settings/ajax/createuser.php');
-$this->create('settings_ajax_removeuser', '/settings/ajax/removeuser.php')
-	->actionInclude('settings/ajax/removeuser.php');
 $this->create('settings_ajax_setquota', '/settings/ajax/setquota.php')
 	->actionInclude('settings/ajax/setquota.php');
-$this->create('settings_ajax_creategroup', '/settings/ajax/creategroup.php')
-	->actionInclude('settings/ajax/creategroup.php');
 $this->create('settings_ajax_togglegroups', '/settings/ajax/togglegroups.php')
 	->actionInclude('settings/ajax/togglegroups.php');
 $this->create('settings_ajax_togglesubadmins', '/settings/ajax/togglesubadmins.php')
 	->actionInclude('settings/ajax/togglesubadmins.php');
-$this->create('settings_ajax_removegroup', '/settings/ajax/removegroup.php')
-	->actionInclude('settings/ajax/removegroup.php');
 $this->create('settings_users_changepassword', '/settings/users/changepassword')
 	->post()
 	->action('OC\Settings\ChangePassword\Controller', 'changeUserPassword');
diff --git a/settings/tests/js/users/deleteHandlerSpec.js b/settings/tests/js/users/deleteHandlerSpec.js
index 6b6328be801196c48590ef30c8b551faac3cb6ab..c6d88b32411fa205b03e44a89c72fe9030c72a2b 100644
--- a/settings/tests/js/users/deleteHandlerSpec.js
+++ b/settings/tests/js/users/deleteHandlerSpec.js
@@ -85,7 +85,7 @@ describe('DeleteHandler tests', function() {
 		// previous one was delete
 		expect(fakeServer.requests.length).toEqual(1);
 		var	request = fakeServer.requests[0];
-		expect(request.url).toEqual(OC.webroot + '/index.php/settings/ajax/dummyendpoint.php');
+		expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid');
 	});
 	it('automatically deletes after timeout', function() {
 		var handler = init(markCallback, removeCallback, undoCallback);
@@ -98,7 +98,7 @@ describe('DeleteHandler tests', function() {
 		clock.tick(3000);
 		expect(fakeServer.requests.length).toEqual(1);
 		var	request = fakeServer.requests[0];
-		expect(request.url).toEqual(OC.webroot + '/index.php/settings/ajax/dummyendpoint.php');
+		expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid');
 	});
 	it('deletes when deleteEntry is called', function() {
 		var handler = init(markCallback, removeCallback, undoCallback);
@@ -107,7 +107,7 @@ describe('DeleteHandler tests', function() {
 		handler.deleteEntry();
 		expect(fakeServer.requests.length).toEqual(1);
 		var	request = fakeServer.requests[0];
-		expect(request.url).toEqual(OC.webroot + '/index.php/settings/ajax/dummyendpoint.php');
+		expect(request.url).toEqual(OC.webroot + '/index.php/dummyendpoint.php/some_uid');
 	});
 	it('cancels deletion when undo is clicked', function() {
 		var handler = init(markCallback, removeCallback, undoCallback);
@@ -135,7 +135,7 @@ describe('DeleteHandler tests', function() {
 		expect(fakeServer.requests.length).toEqual(0);
 	});
 	it('calls removeCallback after successful server side deletion', function() {
-		fakeServer.respondWith(/\/index\.php\/settings\/ajax\/dummyendpoint.php/, [
+		fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [
 			200,
 			{ 'Content-Type': 'application/json' },
 			JSON.stringify({status: 'success'})
@@ -148,7 +148,6 @@ describe('DeleteHandler tests', function() {
 		expect(fakeServer.requests.length).toEqual(1);
 		var request = fakeServer.requests[0];
 		var query = OC.parseQueryString(request.requestBody);
-		expect(query.paramid).toEqual('some_uid');
 
 		expect(removeCallback.calledOnce).toEqual(true);
 		expect(undoCallback.notCalled).toEqual(true);
@@ -157,7 +156,7 @@ describe('DeleteHandler tests', function() {
 	it('calls undoCallback and shows alert after failed server side deletion', function() {
 		// stub t to avoid extra calls
 		var tStub = sinon.stub(window, 't').returns('text');
-		fakeServer.respondWith(/\/index\.php\/settings\/ajax\/dummyendpoint.php/, [
+		fakeServer.respondWith(/\/index\.php\/dummyendpoint.php\/some_uid/, [
 			200,
 			{ 'Content-Type': 'application/json' },
 			JSON.stringify({status: 'error', data: {message: 'test error'}})
@@ -171,7 +170,6 @@ describe('DeleteHandler tests', function() {
 		expect(fakeServer.requests.length).toEqual(1);
 		var request = fakeServer.requests[0];
 		var query = OC.parseQueryString(request.requestBody);
-		expect(query.paramid).toEqual('some_uid');
 
 		expect(removeCallback.notCalled).toEqual(true);
 		expect(undoCallback.calledOnce).toEqual(true);
diff --git a/tests/settings/controller/groupscontrollertest.php b/tests/settings/controller/groupscontrollertest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b6c9d02566b311bbb99f85a9f38d6519bf4abba
--- /dev/null
+++ b/tests/settings/controller/groupscontrollertest.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+namespace OC\Settings\Controller;
+
+use OC\Group\Group;
+use \OC\Settings\Application;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataResponse;
+
+/**
+ * @package OC\Settings\Controller
+ */
+class GroupsControllerTest extends \Test\TestCase {
+
+	/** @var \OCP\AppFramework\IAppContainer */
+	private $container;
+
+	/** @var GroupsController */
+	private $groupsController;
+
+	protected function setUp() {
+		$app = new Application();
+		$this->container = $app->getContainer();
+		$this->container['AppName'] = 'settings';
+		$this->container['GroupManager'] = $this->getMockBuilder('\OCP\IGroupManager')
+			->disableOriginalConstructor()->getMock();
+		$this->container['UserSession'] = $this->getMockBuilder('\OC\User\Session')
+			->disableOriginalConstructor()->getMock();
+		$this->container['L10N'] = $this->getMockBuilder('\OCP\IL10N')
+			->disableOriginalConstructor()->getMock();
+		$this->container['IsAdmin'] = true;
+		$this->container['L10N']
+			->expects($this->any())
+					->method('t')
+					->will($this->returnCallback(function($text, $parameters = array()) {
+							return vsprintf($text, $parameters);
+					}));
+		$this->groupsController = $this->container['GroupsController'];
+
+	}
+
+	/**
+	 * TODO: Since GroupManager uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testIndex() {
+		$firstGroup = $this->getMockBuilder('\OC\Group\Group')
+			->disableOriginalConstructor()->getMock();
+		$firstGroup
+			->method('getGID')
+			->will($this->returnValue('firstGroup'));
+		$firstGroup
+			->method('count')
+			->will($this->returnValue(12));
+		$secondGroup = $this->getMockBuilder('\OC\Group\Group')
+			->disableOriginalConstructor()->getMock();
+		$secondGroup
+			->method('getGID')
+			->will($this->returnValue('secondGroup'));
+		$secondGroup
+			->method('count')
+			->will($this->returnValue(25));
+		$thirdGroup = $this->getMockBuilder('\OC\Group\Group')
+			->disableOriginalConstructor()->getMock();
+		$thirdGroup
+			->method('getGID')
+			->will($this->returnValue('thirdGroup'));
+		$thirdGroup
+			->method('count')
+			->will($this->returnValue(14));
+		$fourthGroup = $this->getMockBuilder('\OC\Group\Group')
+			->disableOriginalConstructor()->getMock();
+		$fourthGroup
+			->method('getGID')
+			->will($this->returnValue('admin'));
+		$fourthGroup
+			->method('count')
+			->will($this->returnValue(18));
+		/** @var \OC\Group\Group[] $groups */
+		$groups = array();
+		$groups[] = $firstGroup;
+		$groups[] = $secondGroup;
+		$groups[] = $thirdGroup;
+		$groups[] = $fourthGroup;
+
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$this->container['UserSession']
+			->expects($this->once())
+			->method('getUser')
+			->will($this->returnValue($user));
+		$user
+			->expects($this->once())
+			->method('getUID')
+			->will($this->returnValue('MyAdminUser'));
+		$this->container['GroupManager']
+			->method('search')
+			->will($this->returnValue($groups));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'data' => array(
+				'adminGroups' => array(
+					0 => array(
+						'id' => 'admin',
+						'name' => 'admin',
+						'usercount' => 18
+					)
+				),
+				'groups' =>
+					array(
+						0 => array(
+							'id' => 'secondGroup',
+							'name' => 'secondGroup',
+							'usercount' => 25
+						),
+						1 => array(
+							'id' => 'thirdGroup',
+							'name' => 'thirdGroup',
+							'usercount' => 14
+						),
+						2 => array(
+							'id' => 'firstGroup',
+							'name' => 'firstGroup',
+							'usercount' => 12
+						)
+					)
+				)
+			)
+		);
+		$response = $this->groupsController->index();
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	public function testCreateWithExistingGroup() {
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('groupExists')
+			->with('ExistingGroup')
+			->will($this->returnValue(true));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'message' => 'Group already exists.'
+			),
+			Http::STATUS_CONFLICT
+		);
+		$response = $this->groupsController->create('ExistingGroup');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	public function testCreateSuccessful() {
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('groupExists')
+			->with('NewGroup')
+			->will($this->returnValue(false));
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('createGroup')
+			->with('NewGroup')
+			->will($this->returnValue(true));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'groupname' => 'NewGroup'
+			),
+			Http::STATUS_CREATED
+		);
+		$response = $this->groupsController->create('NewGroup');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	public function testCreateUnsuccessful() {
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('groupExists')
+			->with('NewGroup')
+			->will($this->returnValue(false));
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('createGroup')
+			->with('NewGroup')
+			->will($this->returnValue(false));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array('message' => 'Unable to add group.')
+			),
+			Http::STATUS_FORBIDDEN
+		);
+		$response = $this->groupsController->create('NewGroup');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	public function testDestroySuccessful() {
+		$group = $this->getMockBuilder('\OC\Group\Group')
+			->disableOriginalConstructor()->getMock();
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('get')
+			->with('ExistingGroup')
+			->will($this->returnValue($group));
+		$group
+			->expects($this->once())
+			->method('delete')
+			->will($this->returnValue(true));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'success',
+				'data' => array('groupname' => 'ExistingGroup')
+			),
+			Http::STATUS_NO_CONTENT
+		);
+		$response = $this->groupsController->destroy('ExistingGroup');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	public function testDestroyUnsuccessful() {
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('get')
+			->with('ExistingGroup')
+			->will($this->returnValue(null));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array('message' => 'Unable to delete group.')
+			),
+			Http::STATUS_FORBIDDEN
+		);
+		$response = $this->groupsController->destroy('ExistingGroup');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+}
diff --git a/tests/settings/controller/userscontrollertest.php b/tests/settings/controller/userscontrollertest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b0683b6dae619740b05b6134a5ab8483c2e53cc
--- /dev/null
+++ b/tests/settings/controller/userscontrollertest.php
@@ -0,0 +1,344 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+namespace OC\Settings\Controller;
+
+use \OC\Settings\Application;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataResponse;
+
+/**
+ * @package OC\Settings\Controller
+ */
+class UsersControllerTest extends \Test\TestCase {
+
+	/** @var \OCP\AppFramework\IAppContainer */
+	private $container;
+
+	/** @var UsersController */
+	private $usersController;
+
+	protected function setUp() {
+		$app = new Application();
+		$this->container = $app->getContainer();
+		$this->container['AppName'] = 'settings';
+		$this->container['GroupManager'] = $this->getMockBuilder('\OCP\IGroupManager')
+			->disableOriginalConstructor()->getMock();
+		$this->container['UserManager'] = $this->getMockBuilder('\OCP\IUserManager')
+			->disableOriginalConstructor()->getMock();
+		$this->container['UserSession'] = $this->getMockBuilder('\OC\User\Session')
+			->disableOriginalConstructor()->getMock();
+		$this->container['L10N'] = $this->getMockBuilder('\OCP\IL10N')
+			->disableOriginalConstructor()->getMock();
+		$this->container['Config'] = $this->getMockBuilder('\OCP\IConfig')
+			->disableOriginalConstructor()->getMock();
+		$this->container['IsAdmin'] = true;
+		$this->container['L10N']
+			->expects($this->any())
+			->method('t')
+			->will($this->returnCallback(function($text, $parameters = array()) {
+				return vsprintf($text, $parameters);
+			}));
+		$this->usersController = $this->container['UsersController'];
+
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testIndex() {
+		$admin = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$admin
+			->method('getLastLogin')
+			->will($this->returnValue(12));
+		$admin
+			->method('getHome')
+			->will($this->returnValue('/home/admin'));
+		$foo = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$foo
+			->method('getLastLogin')
+			->will($this->returnValue(500));
+		$foo
+			->method('getHome')
+			->will($this->returnValue('/home/foo'));
+		$bar = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$bar
+			->method('getLastLogin')
+			->will($this->returnValue(3999));
+		$bar
+			->method('getHome')
+			->will($this->returnValue('/home/bar'));
+
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('displayNamesInGroup')
+			->will($this->returnValue(array('foo' => 'M. Foo', 'admin' => 'S. Admin', 'bar' => 'B. Ar')));
+		$this->container['GroupManager']
+			->expects($this->exactly(3))
+			->method('getUserGroupIds')
+			->will($this->onConsecutiveCalls(array('Users', 'Support'), array('admins', 'Support'), array('External Users')));
+		$this->container['UserManager']
+			->expects($this->exactly(3))
+			->method('get')
+			->will($this->onConsecutiveCalls($foo, $admin, $bar));
+		$this->container['Config']
+			->expects($this->exactly(3))
+			->method('getUserValue')
+			->will($this->onConsecutiveCalls(1024, 404, 2323));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'success',
+				'data' => array(
+					0 => array(
+						'name' => 'foo',
+						'displayname' => 'M. Foo',
+						'groups' => array('Users', 'Support'),
+						'subadmin' => array(),
+						'quota' => 1024,
+						'storageLocation' => '/home/foo',
+						'lastLogin' => 500
+					),
+					1 => array(
+						'name' => 'admin',
+						'displayname' => 'S. Admin',
+						'groups' => array('admins', 'Support'),
+						'subadmin' => array(),
+						'quota' => 404,
+						'storageLocation' => '/home/admin',
+						'lastLogin' => 12
+					),
+					2 => array(
+						'name' => 'bar',
+						'displayname' => 'B. Ar',
+						'groups' => array('External Users'),
+						'subadmin' => array(),
+						'quota' => 2323,
+						'storageLocation' => '/home/bar',
+						'lastLogin' => 3999
+					),
+				)
+			)
+		);
+		$response = $this->usersController->index(0, 10, 'pattern');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testCreateSuccessfulWithoutGroup() {
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$user
+			->method('getHome')
+			->will($this->returnValue('/home/user'));
+
+		$this->container['UserManager']
+			->expects($this->once())
+			->method('createUser')
+			->will($this->onConsecutiveCalls($user));
+
+
+		$expectedResponse = new DataResponse(
+			array(
+				'username' => 'foo',
+				'groups' => null,
+				'storageLocation' => '/home/user'
+			),
+			Http::STATUS_CREATED
+		);
+		$response = $this->usersController->create('foo', 'password', array());
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testCreateSuccessfulWithGroup() {
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$user
+			->method('getHome')
+			->will($this->returnValue('/home/user'));
+		$user
+			->method('getHome')
+			->will($this->returnValue('/home/user'));
+		$existingGroup = $this->getMockBuilder('\OCP\IGroup')
+			->disableOriginalConstructor()->getMock();
+		$existingGroup
+			->expects($this->once())
+			->method('addUser')
+			->with($user);
+		$newGroup = $this->getMockBuilder('\OCP\IGroup')
+			->disableOriginalConstructor()->getMock();
+		$newGroup
+			->expects($this->once())
+			->method('addUser')
+			->with($user);
+
+		$this->container['UserManager']
+			->expects($this->once())
+			->method('createUser')
+			->will($this->onConsecutiveCalls($user));
+		$this->container['GroupManager']
+			->expects($this->exactly(2))
+			->method('get')
+			->will($this->onConsecutiveCalls(null, $existingGroup));
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('createGroup')
+			->with('NewGroup')
+			->will($this->onConsecutiveCalls($newGroup));
+		$this->container['GroupManager']
+			->expects($this->once())
+			->method('getUserGroupIds')
+			->with($user)
+			->will($this->onConsecutiveCalls(array('NewGroup', 'ExistingGroup')));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'username' => 'foo',
+				'groups' => array('NewGroup', 'ExistingGroup'),
+				'storageLocation' => '/home/user'
+			),
+			Http::STATUS_CREATED
+		);
+		$response = $this->usersController->create('foo', 'password', array('NewGroup', 'ExistingGroup'));
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testCreateUnsuccessful() {
+		$this->container['UserManager']
+			->method('createUser')
+			->will($this->throwException(new \Exception()));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'message' => 'Unable to create user.'
+			),
+			Http::STATUS_FORBIDDEN
+		);
+		$response = $this->usersController->create('foo', 'password', array());
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testDestroySelf() {
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$user
+			->expects($this->once())
+			->method('getUID')
+			->will($this->returnValue('myself'));
+		$this->container['UserSession']
+			->method('getUser')
+			->will($this->returnValue($user));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array(
+					'message' => 'Unable to delete user.'
+				)
+			),
+			Http::STATUS_FORBIDDEN
+		);
+		$response = $this->usersController->destroy('myself');
+		$this->assertEquals($expectedResponse, $response);
+	}
+
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testDestroy() {
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$user
+			->expects($this->once())
+			->method('getUID')
+			->will($this->returnValue('Admin'));
+		$toDeleteUser = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$toDeleteUser
+			->expects($this->once())
+			->method('delete')
+			->will($this->returnValue(true));
+		$this->container['UserSession']
+			->method('getUser')
+			->will($this->returnValue($user));
+		$this->container['UserManager']
+			->method('get')
+			->with('UserToDelete')
+			->will($this->returnValue($toDeleteUser));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'success',
+				'data' => array(
+					'username' => 'UserToDelete'
+				)
+			),
+			Http::STATUS_NO_CONTENT
+		);
+		$response = $this->usersController->destroy('UserToDelete');
+		$this->assertEquals($expectedResponse, $response);
+	}
+	/**
+	 * TODO: Since the function uses the static OC_Subadmin class it can't be mocked
+	 * to test for subadmins. Thus the test always assumes you have admin permissions...
+	 */
+	public function testDestroyUnsuccessful() {
+		$user = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$user
+			->expects($this->once())
+			->method('getUID')
+			->will($this->returnValue('Admin'));
+		$toDeleteUser = $this->getMockBuilder('\OC\User\User')
+			->disableOriginalConstructor()->getMock();
+		$toDeleteUser
+			->expects($this->once())
+			->method('delete')
+			->will($this->returnValue(false));
+		$this->container['UserSession']
+			->method('getUser')
+			->will($this->returnValue($user));
+		$this->container['UserManager']
+			->method('get')
+			->with('UserToDelete')
+			->will($this->returnValue($toDeleteUser));
+
+		$expectedResponse = new DataResponse(
+			array(
+				'status' => 'error',
+				'data' => array(
+					'message' => 'Unable to delete user.'
+				)
+			),
+			Http::STATUS_FORBIDDEN
+		);
+		$response = $this->usersController->destroy('UserToDelete');
+		$this->assertEquals($expectedResponse, $response);
+	}
+}
diff --git a/tests/settings/middleware/subadminmiddlewaretest.php b/tests/settings/middleware/subadminmiddlewaretest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5572cfba525e1874d143871d856bd33aa3107d8
--- /dev/null
+++ b/tests/settings/middleware/subadminmiddlewaretest.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * @author Lukas Reschke
+ * @copyright 2014 Lukas Reschke lukas@owncloud.com
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Settings\Middleware;
+
+use OC\AppFramework\Utility\ControllerMethodReflector;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\TemplateResponse;
+
+/**
+ * Verifies whether an user has at least subadmin rights.
+ * To bypass use the `@NoSubadminRequired` annotation
+ *
+ * @package OC\Settings\Middleware
+ */
+class SubadminMiddlewareTest extends \Test\TestCase {
+	/** @var SubadminMiddleware */
+	private $subadminMiddlewareAsSubAdmin;
+	/** @var SubadminMiddleware */
+	private $subadminMiddleware;
+	/** @var ControllerMethodReflector */
+	private $reflector;
+	/** @var Controller */
+	private $controller;
+
+	protected function setUp() {
+		$this->reflector = $this->getMockBuilder('\OC\AppFramework\Utility\ControllerMethodReflector')
+			->disableOriginalConstructor()->getMock();
+		$this->controller = $this->getMockBuilder('\OCP\AppFramework\Controller')
+			->disableOriginalConstructor()->getMock();
+
+		$this->subadminMiddlewareAsSubAdmin = new SubadminMiddleware($this->reflector, true);
+		$this->subadminMiddleware = new SubadminMiddleware($this->reflector, false);
+	}
+
+	/**
+	 * @expectedException \Exception
+	 * @expectedExceptionMessage Logged in user must be a subadmin
+	 */
+	public function testBeforeControllerAsUserWithExemption() {
+		$this->reflector
+			->expects($this->once())
+			->method('hasAnnotation')
+			->with('NoSubadminRequired')
+			->will($this->returnValue(false));
+		$this->subadminMiddleware->beforeController($this->controller, 'foo');
+	}
+
+
+	public function testBeforeControllerAsUserWithoutExemption() {
+		$this->reflector
+			->expects($this->once())
+			->method('hasAnnotation')
+			->with('NoSubadminRequired')
+			->will($this->returnValue(true));
+		$this->subadminMiddleware->beforeController($this->controller, 'foo');
+	}
+
+	public function testBeforeControllerAsSubAdminWithoutExemption() {
+		$this->reflector
+			->expects($this->once())
+			->method('hasAnnotation')
+			->with('NoSubadminRequired')
+			->will($this->returnValue(false));
+		$this->subadminMiddlewareAsSubAdmin->beforeController($this->controller, 'foo');
+	}
+
+	public function testBeforeControllerAsSubAdminWithExemption() {
+		$this->reflector
+			->expects($this->once())
+			->method('hasAnnotation')
+			->with('NoSubadminRequired')
+			->will($this->returnValue(true));
+		$this->subadminMiddlewareAsSubAdmin->beforeController($this->controller, 'foo');
+	}
+
+
+
+
+	public function testAfterException() {
+		$expectedResponse = new TemplateResponse('core', '403', array(), 'guest');
+		$this->assertEquals($expectedResponse, $this->subadminMiddleware->afterException($this->controller, 'foo', new \Exception()));
+	}
+}
\ No newline at end of file