diff --git a/.gitignore b/.gitignore
index 9d8c40b34a1223f32251ed6804b8a560ffeacef6..72e631fa7af51bc8afdb3e98a60c843da8252e68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@
 !/apps/files_versions
 !/apps/user_ldap
 !/apps/user_webdavauth
+!apps/provisioning_api
 /apps/files_external/3rdparty/irodsphp/PHPUnitTest
 /apps/files_external/3rdparty/irodsphp/web
 /apps/files_external/3rdparty/irodsphp/prods/test
diff --git a/apps/provisioning_api/appinfo/info.xml b/apps/provisioning_api/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3f1fa745cf5213a61aec931e925fb0b2a643ff43
--- /dev/null
+++ b/apps/provisioning_api/appinfo/info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?> 
+<info>
+	<id>provisioning_api</id>
+	<name>Provisioning API</name>
+	<description>
+		This application enables a set of APIs that external systems can use to create, edit, delete and query user
+		attributes, query, set and remove groups, set quota and query total storage used in ownCloud. Group admin users
+		can also query ownCloud and perform the same functions as an admin for groups they manage. The API also enables
+		an admin to query for active ownCloud applications, application info, and to enable or disable an app remotely.
+		Once the app is enabled, http requests can be used via a Basic Auth header to perform any of the functions
+		listed above. More information is available in the Provisioning API documentation, including example calls
+		and server responses.
+	</description>
+	<licence>AGPL</licence>
+	<author>Tom Needham</author>
+	<requiremin>8</requiremin>
+	<shipped>true</shipped>
+	<default_enable/>
+	<documentation>
+		<admin>admin-provisioning-api</admin>
+	</documentation>
+</info>
diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php
new file mode 100644
index 0000000000000000000000000000000000000000..7c626501d186125bea28258149d1690834164488
--- /dev/null
+++ b/apps/provisioning_api/appinfo/routes.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@owncloud.com>
+ * @author Jörn Friedrich Dreyer <jfd@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/>.
+ *
+ */
+
+// Users
+OCP\API::register('get', '/cloud/users', array('OCA\Provisioning_API\Users', 'getUsers'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('post', '/cloud/users', array('OCA\Provisioning_API\Users', 'addUser'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('get', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'getUser'), 'provisioning_api', OC_API::USER_AUTH);
+OCP\API::register('put', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'editUser'), 'provisioning_api', OC_API::USER_AUTH);
+OCP\API::register('delete', '/cloud/users/{userid}', array('OCA\Provisioning_API\Users', 'deleteUser'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('get', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'getUsersGroups'), 'provisioning_api', OC_API::USER_AUTH);
+OCP\API::register('post', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'addToGroup'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('delete', '/cloud/users/{userid}/groups', array('OCA\Provisioning_API\Users', 'removeFromGroup'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('post', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'addSubAdmin'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('delete', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'removeSubAdmin'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('get', '/cloud/users/{userid}/subadmins', array('OCA\Provisioning_API\Users', 'getUserSubAdminGroups'), 'provisioning_api', OC_API::ADMIN_AUTH);
+
+// Groups
+OCP\API::register('get', '/cloud/groups', array('OCA\Provisioning_API\Groups', 'getGroups'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('post', '/cloud/groups', array('OCA\Provisioning_API\Groups', 'addGroup'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('get', '/cloud/groups/{groupid}', array('OCA\Provisioning_API\Groups', 'getGroup'), 'provisioning_api', OC_API::SUBADMIN_AUTH);
+OCP\API::register('delete', '/cloud/groups/{groupid}', array('OCA\Provisioning_API\Groups', 'deleteGroup'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('get', '/cloud/groups/{groupid}/subadmins', array('OCA\Provisioning_API\Groups', 'getSubAdminsOfGroup'), 'provisioning_api', OC_API::ADMIN_AUTH);
+
+// Apps
+OCP\API::register('get', '/cloud/apps', array('OCA\Provisioning_API\Apps', 'getApps'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('get', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'getAppInfo'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('post', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'enable'), 'provisioning_api', OC_API::ADMIN_AUTH);
+OCP\API::register('delete', '/cloud/apps/{appid}', array('OCA\Provisioning_API\Apps', 'disable'), 'provisioning_api', OC_API::ADMIN_AUTH);
diff --git a/apps/provisioning_api/appinfo/version b/apps/provisioning_api/appinfo/version
new file mode 100644
index 0000000000000000000000000000000000000000..3b04cfb60da13a716867848ebeb2191a164887d9
--- /dev/null
+++ b/apps/provisioning_api/appinfo/version
@@ -0,0 +1 @@
+0.2
diff --git a/apps/provisioning_api/lib/apps.php b/apps/provisioning_api/lib/apps.php
new file mode 100644
index 0000000000000000000000000000000000000000..f44ccd86a1381f3afa135723010f9441707cff5e
--- /dev/null
+++ b/apps/provisioning_api/lib/apps.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@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/>.
+ *
+ */
+
+namespace OCA\Provisioning_API;
+
+use \OC_OCS_Result;
+use \OC_App;
+
+class Apps {
+
+	public static function getApps($parameters){
+		$apps = OC_App::listAllApps();
+		$list = array();
+		foreach($apps as $app) {
+			$list[] = $app['id'];
+		}
+		$filter = isset($_GET['filter']) ? $_GET['filter'] : false;
+		if($filter){
+			switch($filter){
+				case 'enabled':
+					return new OC_OCS_Result(array('apps' => \OC_App::getEnabledApps()));
+					break;
+				case 'disabled':
+					$enabled = OC_App::getEnabledApps();
+					return new OC_OCS_Result(array('apps' => array_diff($list, $enabled)));
+					break;
+				default:
+					// Invalid filter variable
+					return new OC_OCS_Result(null, 101);
+					break;
+			}
+
+		} else {
+			return new OC_OCS_Result(array('apps' => $list));
+		}
+	}
+
+	public static function getAppInfo($parameters){
+		$app = $parameters['appid'];
+		$info = OC_App::getAppInfo($app);
+		if(!is_null($info)) {
+			return new OC_OCS_Result(OC_App::getAppInfo($app));
+		} else {
+			return new OC_OCS_Result(null, \OC_API::RESPOND_NOT_FOUND, 'The request app was not found');
+		}
+	}
+
+	public static function enable($parameters){
+		$app = $parameters['appid'];
+		OC_App::enable($app);
+		return new OC_OCS_Result(null, 100);
+	}
+
+	public static function disable($parameters){
+		$app = $parameters['appid'];
+		OC_App::disable($app);
+		return new OC_OCS_Result(null, 100);
+	}
+
+}
diff --git a/apps/provisioning_api/lib/groups.php b/apps/provisioning_api/lib/groups.php
new file mode 100644
index 0000000000000000000000000000000000000000..4440c5bf50906f34b39d23acf5001ac2a75da3f5
--- /dev/null
+++ b/apps/provisioning_api/lib/groups.php
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@owncloud.com>
+ * @author Bart Visscher
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Provisioning_API;
+
+use \OC_OCS_Result;
+use \OC_Group;
+use \OC_SubAdmin;
+
+class Groups{
+
+	/**
+	 * returns a list of groups
+	 */
+	public static function getGroups($parameters){
+		$search = !empty($_GET['search']) ? $_GET['search'] : '';
+		$limit = !empty($_GET['limit']) ? $_GET['limit'] : null;
+		$offset = !empty($_GET['offset']) ? $_GET['offset'] : null;
+		return new OC_OCS_Result(array('groups' => OC_Group::getGroups($search, $limit, $offset)));
+	}
+
+	/**
+	 * returns an array of users in the group specified
+	 */
+	public static function getGroup($parameters){
+		// Check the group exists
+		if(!OC_Group::groupExists($parameters['groupid'])){
+			return new OC_OCS_Result(null, \OC_API::RESPOND_NOT_FOUND, 'The requested group could not be found');
+		}
+		// Check subadmin has access to this group
+		if(\OC_User::isAdminUser(\OC_User::getUser())
+			|| in_array($parameters['groupid'], \OC_SubAdmin::getSubAdminsGroups(\OC_User::getUser()))){
+			return new OC_OCS_Result(array('users' => OC_Group::usersInGroup($parameters['groupid'])));
+		} else {
+			return new OC_OCS_Result(null, \OC_API::RESPOND_UNAUTHORISED, 'User does not have access to specified group');
+		}
+	}
+
+	/**
+	 * creates a new group
+	 */
+	public static function addGroup($parameters){
+		// Validate name
+		$groupid = isset($_POST['groupid']) ? $_POST['groupid'] : '';
+		if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $groupid ) || empty($groupid)){
+			\OC_Log::write('provisioning_api', 'Attempt made to create group using invalid characters.', \OC_Log::ERROR);
+			return new OC_OCS_Result(null, 101, 'Invalid group name');
+		}
+		// Check if it exists
+		if(OC_Group::groupExists($groupid)){
+			return new OC_OCS_Result(null, 102);
+		}
+		if(OC_Group::createGroup($groupid)){
+			return new OC_OCS_Result(null, 100);
+		} else {
+			return new OC_OCS_Result(null, 103);
+		}
+	}
+
+	public static function deleteGroup($parameters){
+		// Check it exists
+		if(!OC_Group::groupExists($parameters['groupid'])){
+			return new OC_OCS_Result(null, 101);
+		} else if($parameters['groupid'] == 'admin' || !OC_Group::deleteGroup($parameters['groupid'])){
+			// Cannot delete admin group
+			return new OC_OCS_Result(null, 102);
+		} else {
+			return new OC_OCS_Result(null, 100);
+		}
+	}
+
+	public static function getSubAdminsOfGroup($parameters) {
+		$group = $parameters['groupid'];
+		// Check group exists
+		if(!OC_Group::groupExists($group)) {
+			return new OC_OCS_Result(null, 101, 'Group does not exist');
+		}
+		// Go
+		if(!$subadmins = OC_Subadmin::getGroupsSubAdmins($group)) {
+			return new OC_OCS_Result(null, 102, 'Unknown error occured');
+		} else {
+			return new OC_OCS_Result($subadmins);
+		}
+	}
+
+}
diff --git a/apps/provisioning_api/lib/users.php b/apps/provisioning_api/lib/users.php
new file mode 100644
index 0000000000000000000000000000000000000000..891264a908ac52c7b39df3cb61fd6faa13e71757
--- /dev/null
+++ b/apps/provisioning_api/lib/users.php
@@ -0,0 +1,346 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@owncloud.com>
+ * @author Thomas Müller <deepdiver@owncloud.com>
+ * @author Bart Visscher
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Provisioning_API;
+
+use \OC_OCS_Result;
+use \OC_SubAdmin;
+use \OC_User;
+use \OC_Group;
+use \OC_Helper;
+
+class Users {
+
+	/**
+	 * returns a list of users
+	 */
+	public static function getUsers(){
+		$search = !empty($_GET['search']) ? $_GET['search'] : '';
+		$limit = !empty($_GET['limit']) ? $_GET['limit'] : null;
+		$offset = !empty($_GET['offset']) ? $_GET['offset'] : null;
+		return new OC_OCS_Result(array('users' => OC_User::getUsers($search, $limit, $offset)));
+	}
+
+	public static function addUser(){
+		$userId = isset($_POST['userid']) ? $_POST['userid'] : null;
+		$password = isset($_POST['password']) ? $_POST['password'] : null;
+		if(OC_User::userExists($userId)) {
+			\OC_Log::write('ocs_api', 'Failed addUser attempt: User already exists.', \OC_Log::ERROR);
+			return new OC_OCS_Result(null, 102, 'User already exists');
+		} else {
+			try {
+				OC_User::createUser($userId, $password);
+				\OC_Log::write('ocs_api', 'Successful addUser call with userid: '.$_POST['userid'], \OC_Log::INFO);
+				return new OC_OCS_Result(null, 100);
+			} catch (\Exception $e) {
+				\OC_Log::write('ocs_api', 'Failed addUser attempt with exception: '.$e->getMessage(), \OC_Log::ERROR);
+				return new OC_OCS_Result(null, 101, 'Bad request');
+			}
+		}
+	}
+
+	/**
+	 * gets user info
+	 */
+	public static function getUser($parameters){
+		$userId = $parameters['userid'];
+		// Admin? Or SubAdmin?
+		if(OC_User::isAdminUser(OC_User::getUser()) || OC_SubAdmin::isUserAccessible(OC_User::getUser(), $userId)) {
+			// Check they exist
+			if(!OC_user::userExists($userId)) {
+				return new OC_OCS_Result(null, \OC_API::RESPOND_NOT_FOUND, 'The requested user could not be found');
+			}
+			// Show all
+			$return = array(
+				'email',
+				'enabled',
+				);
+			if(OC_User::getUser() != $userId) {
+				$return[] = 'quota';
+			}
+		} else {
+			// Check they are looking up themselves
+			if(OC_User::getUser() != $userId) {
+				return new OC_OCS_Result(null, \OC_API::RESPOND_UNAUTHORISED);
+			}
+			// Return some additional information compared to the core route
+			$return = array(
+				'email',
+				'displayname',
+				);
+		}
+
+		$config = \OC::$server->getConfig();
+
+		// Find the data
+		$data = array();
+		\OC_Util::tearDownFS();
+		\OC_Util::setupFS($userId);
+		$storage = OC_Helper::getStorageInfo('/');
+		$data['quota'] = array(
+			'free' =>  $storage['free'],
+			'used' =>  $storage['used'],
+			'total' =>  $storage['total'],
+			'relative' => $storage['relative'],
+			);
+		$data['enabled'] = $config->getUserValue($userId, 'core', 'enabled', 'true');
+		$data['email'] = $config->getUserValue($userId, 'settings', 'email');
+		$data['displayname'] = OC_User::getDisplayName($parameters['userid']);
+
+		// Return the appropriate data
+		$responseData = array();
+		foreach($return as $key) {
+			$responseData[$key] = $data[$key];
+		}
+
+		return new OC_OCS_Result($responseData);
+	}
+
+	/** 
+	 * edit users
+	 */
+	public static function editUser($parameters){
+		$userId = $parameters['userid'];
+		if($userId === OC_User::getUser()) {
+			// Editing self (display, email)
+			$permittedFields[] = 'display';
+			$permittedFields[] = 'email';
+			$permittedFields[] = 'password';
+			// If admin they can edit their own quota
+			if(OC_User::isAdminUser(OC_User::getUser())) {
+				$permittedFields[] = 'quota';
+			}
+		} else {
+			// Check if admin / subadmin
+			if(OC_SubAdmin::isUserAccessible(OC_User::getUser(), $userId)
+			|| OC_User::isAdminUser(OC_User::getUser())) {
+				// They have permissions over the user
+				$permittedFields[] = 'display';
+				$permittedFields[] = 'quota';
+				$permittedFields[] = 'password';
+				$permittedFields[] = 'email';
+			} else {
+				// No rights
+				return new OC_OCS_Result(null, 997);
+			}
+		}
+		// Check if permitted to edit this field
+		if(!in_array($parameters['_put']['key'], $permittedFields)) {
+			return new OC_OCS_Result(null, 997);
+		}
+		// Process the edit
+		switch($parameters['_put']['key']){
+			case 'display':
+				OC_User::setDisplayName($userId, $parameters['_put']['value']);
+				break;
+			case 'quota':
+				$quota = $parameters['_put']['value'];
+				if($quota !== 'none' and $quota !== 'default') {
+					$quota = OC_Helper::computerFileSize($quota);
+					if($quota == 0) {
+						$quota = 'default';
+					}else if($quota == -1){
+						$quota = 'none';
+					} else {
+						$quota = OC_Helper::humanFileSize($quota);
+					}
+				}
+				\OC::$server->getConfig()->setUserValue($userId, 'files', 'quota', $quota);
+				break;
+			case 'password':
+				OC_User::setPassword($userId, $parameters['_put']['value']);
+				break;
+			case 'email':
+				if(filter_var($parameters['_put']['value'], FILTER_VALIDATE_EMAIL)) {
+					\OC::$server->getConfig()->setUserValue($userId, 'settings', 'email', $parameters['_put']['value']);
+				} else {
+					return new OC_OCS_Result(null, 102);
+				}
+				break;
+			default:
+				return new OC_OCS_Result(null, 103);
+				break;
+		}
+		return new OC_OCS_Result(null, 100);
+	}
+
+	public static function deleteUser($parameters){
+		if(!OC_User::userExists($parameters['userid']) 
+		|| $parameters['userid'] === OC_User::getUser()) {
+			return new OC_OCS_Result(null, 101);
+		}
+		// If not permitted
+		if(!OC_User::isAdminUser(OC_User::getUser()) && !OC_SubAdmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) {
+			return new OC_OCS_Result(null, 997);
+		}
+		// Go ahead with the delete
+		if(OC_User::deleteUser($parameters['userid'])) {
+			return new OC_OCS_Result(null, 100);
+		} else {
+			return new OC_OCS_Result(null, 101);
+		}
+	}
+
+	public static function getUsersGroups($parameters){
+		if($parameters['userid'] === OC_User::getUser() || OC_User::isAdminUser(OC_User::getUser())) {
+			// Self lookup or admin lookup
+			return new OC_OCS_Result(array('groups' => OC_Group::getUserGroups($parameters['userid'])));
+		} else {
+			// Looking up someone else
+			if(OC_SubAdmin::isUserAccessible(OC_User::getUser(), $parameters['userid'])) {
+				// Return the group that the method caller is subadmin of for the user in question
+				$groups = array_intersect(OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()), OC_Group::getUserGroups($parameters['userid']));
+				return new OC_OCS_Result(array('groups' => $groups));
+			} else {
+				// Not permitted
+				return new OC_OCS_Result(null, 997);
+			}
+		}
+		
+	}
+
+	public static function addToGroup($parameters){
+		$group = !empty($_POST['groupid']) ? $_POST['groupid'] : null;
+		if(is_null($group)){
+			return new OC_OCS_Result(null, 101);
+		}
+		// Check they're an admin
+		if(!OC_Group::inGroup(OC_User::getUser(), 'admin')){
+			// This user doesn't have rights to add a user to this group
+			return new OC_OCS_Result(null, \OC_API::RESPOND_UNAUTHORISED);
+		}
+		// Check if the group exists
+		if(!OC_Group::groupExists($group)){
+			return new OC_OCS_Result(null, 102);
+		}
+		// Check if the user exists
+		if(!OC_User::userExists($parameters['userid'])){
+			return new OC_OCS_Result(null, 103);
+		}
+		// Add user to group
+		return OC_Group::addToGroup($parameters['userid'], $group) ? new OC_OCS_Result(null, 100) : new OC_OCS_Result(null, 105);
+	}
+
+	public static function removeFromGroup($parameters){
+		$group = !empty($parameters['_delete']['groupid']) ? $parameters['_delete']['groupid'] : null;
+		if(is_null($group)){
+			return new OC_OCS_Result(null, 101);
+		}
+		// If they're not an admin, check they are a subadmin of the group in question
+		if(!OC_Group::inGroup(OC_User::getUser(), 'admin') && !OC_SubAdmin::isSubAdminofGroup(OC_User::getUser(), $group)){
+			return new OC_OCS_Result(null, 104);
+		}
+		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
+		if($parameters['userid'] === OC_User::getUser()){
+			if(OC_Group::inGroup(OC_User::getUser(), 'admin')){
+				if($group === 'admin'){
+					return new OC_OCS_Result(null, 105, 'Cannot remove yourself from the admin group');
+				}
+			} else {
+				// Not an admin, check they are not removing themself from their subadmin group
+				if(in_array($group, OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()))){
+					return new OC_OCS_Result(null, 105, 'Cannot remove yourself from this group as you are a SubAdmin');
+				}
+			}
+		}
+		// Check if the group exists
+		if(!OC_Group::groupExists($group)){
+			return new OC_OCS_Result(null, 102);
+		}
+		// Check if the user exists
+		if(!OC_User::userExists($parameters['userid'])){
+			return new OC_OCS_Result(null, 103);
+		}
+		// Remove user from group
+		return OC_Group::removeFromGroup($parameters['userid'], $group) ? new OC_OCS_Result(null, 100) : new OC_OCS_Result(null, 105);
+	}
+
+	/**
+	 * Creates a subadmin
+	 */
+	public static function addSubAdmin($parameters) {
+		$group = $_POST['groupid'];
+		$user = $parameters['userid'];	
+		// Check if the user exists
+		if(!OC_User::userExists($user)) {
+			return new OC_OCS_Result(null, 101, 'User does not exist');
+		}
+		// Check if group exists
+		if(!OC_Group::groupExists($group)) {
+			return new OC_OCS_Result(null, 102, 'Group:'.$group.' does not exist');
+		}
+		// Check if trying to make subadmin of admin group
+		if(strtolower($group) == 'admin') {
+			return new OC_OCS_Result(null, 103, 'Cannot create subadmins for admin group');
+		}
+		// Go
+		if(OC_Subadmin::createSubAdmin($user, $group)) {
+			return new OC_OCS_Result(null, 100);
+		} else {
+			return new OC_OCS_Result(null, 103, 'Unknown error occured');
+		}
+
+	}
+
+	/**
+	 * Removes a subadmin from a group
+	 */
+	public static function removeSubAdmin($parameters) {
+		$group = $parameters['_delete']['groupid'];
+		$user = $parameters['userid'];	
+		// Check if the user exists
+		if(!OC_User::userExists($user)) {
+			return new OC_OCS_Result(null, 101, 'User does not exist');
+		}
+		// Check if they are a subadmin of this said group
+		if(!OC_SubAdmin::isSubAdminofGroup($user, $group)) {
+			return new OC_OCS_Result(null, 102, 'User is not a subadmin of this group');
+		}
+		// Go
+		if(OC_Subadmin::deleteSubAdmin($user, $group)) {
+			return new OC_OCS_Result(null, 100);
+		} else {
+			return new OC_OCS_Result(null, 103, 'Unknown error occurred');
+		}
+	}
+
+	/**
+	 * @Get the groups a user is a subadmin of
+	 */
+	public static function getUserSubAdminGroups($parameters) {
+		$user = $parameters['userid'];
+		// Check if the user exists
+		if(!OC_User::userExists($user)) {
+			return new OC_OCS_Result(null, 101, 'User does not exist');
+		}
+		// Get the subadmin groups
+		if(!$groups = OC_SubAdmin::getSubAdminsGroups($user)) {
+			return new OC_OCS_Result(null, 102, 'Unknown error occurred');
+		} else {
+			return new OC_OCS_Result($groups);
+		}
+	}
+}
diff --git a/apps/provisioning_api/tests/AppsTest.php b/apps/provisioning_api/tests/AppsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a269450ccdbba4fde7311600e7c5604f5c8a24f
--- /dev/null
+++ b/apps/provisioning_api/tests/AppsTest.php
@@ -0,0 +1,101 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@owncloud.com>
+ * @author Thomas Müller <deepdiver@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/>.
+ *
+ */
+
+class Test_Provisioning_Api_Apps extends PHPUnit_Framework_TestCase {
+
+	private $users = array();
+
+	/**
+	 * Generates a temp user
+	 * @param $num int number of users to generate
+	 */
+	function generateUsers($num=1) {
+		for($i=0; $i<$num; $i++) {
+			$user = uniqid();
+			\OC_User::createUser($user, 'password');
+			$this->users[] = $user;
+			$users[] = $user;
+		}
+		return count($users) == 1 ? reset($users) : $users;
+	}
+
+	function testGetAppInfo() {
+
+		$result = \OCA\provisioning_API\Apps::getAppInfo(array('appid' => 'provisioning_api'));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+
+	}
+
+	function testGetAppInfoOnBadAppID() {
+
+		$result = \OCA\provisioning_API\Apps::getAppInfo(array('appid' => 'not_provisioning_api'));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(\OC_API::RESPOND_NOT_FOUND, $result->getStatusCode());
+
+	}
+
+	public function testGetApps() {
+
+		$user = $this->generateUsers();
+		\OC_Group::addToGroup($user, 'admin');
+		\OC_User::setUserId($user);
+
+		$result = \OCA\provisioning_API\Apps::getApps(array());
+
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals(count(\OC_App::listAllApps()), count($data['apps']));
+
+	}
+
+	public function testGetAppsEnabled() {
+
+		$_GET['filter'] = 'enabled';
+		$result = \OCA\provisioning_API\Apps::getApps(array('filter' => 'enabled'));
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals(count(\OC_App::getEnabledApps()), count($data['apps']));
+
+	}
+
+	public function testGetAppsDisabled() {
+
+		$_GET['filter'] = 'disabled';
+		$result = \OCA\provisioning_API\Apps::getApps(array('filter' => 'disabled'));
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$apps = \OC_App::listAllApps();
+		$list =  array();
+		foreach($apps as $app) {
+			$list[] = $app['id'];
+		}
+		$disabled = array_diff($list, \OC_App::getEnabledApps());
+		$this->assertEquals(count($disabled), count($data['apps']));
+
+	}
+
+}
diff --git a/apps/provisioning_api/tests/groupsTest.php b/apps/provisioning_api/tests/groupsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e4800dfef161a0728e7f8c25268f2e50b960c77
--- /dev/null
+++ b/apps/provisioning_api/tests/groupsTest.php
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@owncloud.com>
+ * @author Thomas Müller <deepdiver@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/>.
+ *
+ */
+
+class Test_Provisioning_Api_Groups extends PHPUnit_Framework_TestCase {
+
+	private $users = array();
+
+	/**
+	 * Generates a temp user
+	 * @param $num int number of users to generate
+	 */
+	function generateUsers($num=1) {
+		for($i=0; $i<$num; $i++) {
+			$user = uniqid();
+			\OC_User::createUser($user, 'password');
+			$this->users[] = $user;
+			$users[] = $user;
+		}
+		return count($users) == 1 ? reset($users) : $users;
+	}
+
+	function testGetGroupAsUser() {
+
+		$users = $this->generateUsers(2);
+		\OC_User::setUserId($users[0]);
+
+		$group = uniqid();
+		\OC_Group::createGroup($group);
+		\OC_Group::addToGroup($users[1], $group);
+
+		$result = \OCA\provisioning_api\Groups::getGroup(array(
+			'groupid' => $group,
+		));
+
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(\OC_API::RESPOND_UNAUTHORISED, $result->getStatusCode());
+
+	}
+
+	function testGetGroupAsSubadmin() {
+
+		$users = $this->generateUsers(2);
+		\OC_User::setUserId($users[0]);
+
+		$group = uniqid();
+		\OC_Group::createGroup($group);
+		\OC_Group::addToGroup($users[1], $group);
+		\OC_Group::addToGroup($users[0], $group);
+
+		\OC_SubAdmin::createSubAdmin($users[0], $group);
+
+		$result = \OCA\provisioning_api\Groups::getGroup(array(
+			'groupid' => $group,
+		));
+
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals(array('users' => $users), $result->getData());
+
+	}
+
+	function testGetGroupAsIrrelevantSubadmin() {
+
+		$users = $this->generateUsers(2);
+		\OC_User::setUserId($users[0]);
+
+		$group = uniqid();
+		\OC_Group::createGroup($group);
+		$group2 = uniqid();
+		\OC_Group::createGroup($group2);
+		\OC_Group::addToGroup($users[1], $group);
+		\OC_Group::addToGroup($users[0], $group2);
+
+		\OC_SubAdmin::createSubAdmin($users[0], $group2);
+
+		$result = \OCA\provisioning_api\Groups::getGroup(array(
+			'groupid' => $group,
+		));
+
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(\OC_API::RESPOND_UNAUTHORISED, $result->getStatusCode());
+
+	}
+
+	function testGetGroupAsAdmin() {
+
+		$users = $this->generateUsers(2);
+		\OC_User::setUserId($users[0]);
+
+		$group = uniqid();
+		\OC_Group::createGroup($group);
+
+		\OC_Group::addToGroup($users[1], $group);
+		\OC_Group::addToGroup($users[0], 'admin');
+
+		$result = \OCA\provisioning_api\Groups::getGroup(array(
+			'groupid' => $group,
+		));
+
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals(array('users' => array($users[1])), $result->getData());
+
+	}
+
+	function testGetSubAdminsOfGroup() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_SubAdmin::createSubAdmin($user2, $group1);
+		$result = \OCA\provisioning_api\Groups::getSubAdminsOfGroup(array(
+			'groupid' => $group1,
+		));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($user2, reset($data));
+		OC_Group::deleteGroup($group1);
+
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		$result = \OCA\provisioning_api\Groups::getSubAdminsOfGroup(array(
+			'groupid' => uniqid(),
+		));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(101, $result->getStatusCode());
+	}
+
+	/**
+	 * Remove all the temporary users
+	 */
+	function tearDown() {
+		foreach($this->users as $user) {
+			\OC_User::deleteUser($user);
+		}
+	}
+
+
+}
diff --git a/apps/provisioning_api/tests/usersTest.php b/apps/provisioning_api/tests/usersTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b464a2e5fd036d8811aebd144c88a845eec4d97f
--- /dev/null
+++ b/apps/provisioning_api/tests/usersTest.php
@@ -0,0 +1,795 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @copyright (C) 2014 ownCloud, Inc.
+ *
+ * @author Tom <tom@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/>.
+ *
+ */
+
+class Test_Provisioning_Api_Users extends PHPUnit_Framework_TestCase {
+
+	private $users = array();
+
+	/**
+	 * Generates a temp user
+	 * @param $num int number of users to generate
+	 */
+	function generateUsers($num=1) {
+		$users = array();
+		for($i=0; $i<$num; $i++) {
+			$user = uniqid();
+			\OC_User::createUser($user, 'password');
+			$this->users[] = $user;
+			$users[] = $user;
+		}
+		return count($users) == 1 ? reset($users) : $users;
+	}
+
+	function resetParams() {
+		$_GET = null;
+		$_POST = null;
+	}
+
+	// Test getting the list of users
+	function testGetUsers() {
+		$result = \OCA\provisioning_API\Users::getUsers(array());
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$count = $result->getData();
+		$count = count($count['users']);
+		$this->assertEquals(count(\OC_User::getUsers()), $count);
+
+		$user = $this->generateUsers();
+		$_GET['search'] = $user;
+		$result = \OCA\provisioning_API\Users::getUsers(array());
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($user, reset($data['users']));
+
+		// Add several users
+		$this->generateUsers(10);
+		$this->resetParams();
+		$_GET['limit'] = 2;
+		$result = \OCA\provisioning_API\Users::getUsers(array());
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$count = $result->getData();
+		$count = count($count['users']);
+		$this->assertEquals(2, $count);
+
+		$this->resetParams();
+		$_GET['limit'] = 1;
+		$_GET['offset'] = 1;
+		$result = \OCA\provisioning_API\Users::getUsers(array());
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals(\OC_User::getUsers('', 1, 1), $data['users']);
+	}
+
+	function testAddUser() {
+		$this->resetParams();
+		$_POST['userid'] = uniqid();
+		$_POST['password'] = 'password';
+		$result = \OCA\provisioning_API\Users::addUser(array());
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertTrue(\OC_User::userExists($_POST['userid']));
+		$this->assertEquals($_POST['userid'], \OC_User::checkPassword($_POST['userid'], $_POST['password']));
+		$this->users[] = $_POST['userid'];
+	}
+
+	function testGetUserOnSelf() {
+		$user = $this->generateUsers();
+		\OC_User::setUserId($user);
+		$params['userid'] = $user;
+		$result = \OCA\provisioning_API\Users::getUser($params);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+	}
+
+	function testGetUserOnNonExistingUser() {
+		$user = $this->generateUsers();
+		\OC_Group::addToGroup($user, 'admin');
+		\OC_User::setUserId($user);
+		$params = array();
+		$params['userid'] = uniqid();
+		while(\OC_User::userExists($params['userid'])) {
+			$params['userid'] = uniqid();
+		}
+		$result = \OCA\provisioning_API\Users::getUser($params);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(\OC_API::RESPOND_NOT_FOUND, $result->getStatusCode());
+
+	}
+
+	function testGetUserOnOtherUser() {
+		$users = $this->generateUsers(2);
+		$params['userid'] = $users[0];
+		\OC_User::setUserId($users[1]);
+		$result = \OCA\provisioning_API\Users::getUser($params);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+
+		// Now as as admin
+		$users = $this->generateUsers(2);
+		$params['userid'] = $users[0];
+		\OC_Group::addToGroup($users[1], 'admin');
+		\OC_User::setUserId($users[1]);
+		$result = \OCA\provisioning_API\Users::getUser($params);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals(\OC::$server->getConfig()->getUserValue($users[0], 'core', 'enabled', 'true'), $data['enabled']);
+	}
+
+	function testEditOwnDisplayName() {
+
+		// Test editing own name
+		$user = $this->generateUsers();
+		\OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user,
+				'_put' => array(
+					'key' => 'display',
+					'value' => 'newname',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals('newname', \OC_User::getDisplayName($user));
+
+	}
+
+	function testAdminEditDisplayNameOfUser() {
+
+		// Test admin editing users name
+		$user = $this->generateUsers();
+		\OC_Group::addToGroup($user, 'admin');
+		\OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user2,
+				'_put' => array(
+					'key' => 'display',
+					'value' => 'newname',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals('newname', \OC_User::getDisplayName($user2));
+
+	}
+
+	function testUserEditOtherUserDisplayName() {
+
+		// Test editing other users name
+		$user = $this->generateUsers();
+		\OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user2,
+				'_put' => array(
+					'key' => 'display',
+					'value' => 'newname',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+
+	}
+
+	function testEditOwnQuota() {
+		$user = $this->generateUsers();
+		\OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user,
+				'_put' => array(
+					'key' => 'quota',
+					'value' => '20G',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testAdminEditOwnQuota() {
+		$user = $this->generateUsers();
+		\OC_Group::addToGroup($user, 'admin');
+		\OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user,
+				'_put' => array(
+					'key' => 'quota',
+					'value' => '20G',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+	}
+
+	function testAdminEditOtherUserQuota() {
+		$user = $this->generateUsers();
+		\OC_Group::addToGroup($user, 'admin');
+		\OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user2,
+				'_put' => array(
+					'key' => 'quota',
+					'value' => '20G',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+	}
+
+	function testUserEditOtherUserQuota() {
+		$user = $this->generateUsers();
+		\OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user2,
+				'_put' => array(
+					'key' => 'quota',
+					'value' => '20G',
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testUserEditOwnEmail() {
+		$user = $this->generateUsers();
+		$email = 'test@example.com';
+		\OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $user,
+				'_put' => array(
+					'key' => 'email',
+					'value' => $email,
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals($email, \OC::$server->getConfig()->getUserValue($user, 'settings', 'email', null));
+	}
+
+	function testUserEditOtherUserEmailAsUser() {
+		$users = $this->generateUsers(2);
+		$email = 'test@example.com';
+		\OC_User::setUserId($users[0]);
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $users[1],
+				'_put' => array(
+					'key' => 'email',
+					'value' => $email,
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testUserEditOtherUserEmailAsAdmin() {
+		$users = $this->generateUsers(2);
+		$email = 'test@example.com';
+		\OC_User::setUserId($users[0]);
+		\OC_Group::addToGroup($users[0], 'admin');
+		$result = \OCA\provisioning_API\Users::editUser(
+			array(
+				'userid' => $users[1],
+				'_put' => array(
+					'key' => 'email',
+					'value' => $email,
+					),
+				)
+			);
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertEquals($email, \OC::$server->getConfig()->getUserValue($users[1], 'settings', 'email', null));
+	}
+
+	function testDeleteSelf() {
+		$user = $this->generateUsers();
+		OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testDeleteOtherAsUser() {
+		$user = $this->generateUsers();
+		OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testDeleteOtherAsSubAdmin() {
+		$user = $this->generateUsers();
+		OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		OC_Group::addToGroup($user, $group);
+		OC_Group::addToGroup($user2, $group);
+		OC_SubAdmin::createSubAdmin($user, $group);
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		OC_Group::deleteGroup($group);
+	}
+
+	function testDeleteOtherAsIrelevantSubAdmin() {
+		$user = $this->generateUsers();
+		OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$group = uniqid();
+		$group2 = uniqid();
+		OC_Group::createGroup($group);
+		OC_Group::createGroup($group2);
+		OC_Group::addToGroup($user, $group);
+		OC_Group::addToGroup($user2, $group2);
+		OC_SubAdmin::createSubAdmin($user, $group);
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		OC_Group::deleteGroup($group);
+		OC_Group::deleteGroup($group2);
+	}
+
+	function testDeleteOtherAsAdmin() {
+		$user = $this->generateUsers();
+		OC_Group::addToGroup($user, 'admin');
+		OC_User::setUserId($user);
+		$user2 = $this->generateUsers();
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+	}
+
+	function testDeleteSelfAsAdmin() {
+		$user = $this->generateUsers();
+		OC_Group::addToGroup($user, 'admin');
+		OC_User::setUserId($user);
+		$result = \OCA\provisioning_API\Users::deleteUser(array(
+			'userid' => $user,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+	}
+
+	function testGetUsersGroupsOnSelf() {
+		$user = $this->generateUsers();
+		OC_User::setUserId($user);
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		OC_Group::addToGroup($user, $group);
+		$result = \OCA\provisioning_API\Users::getUsersGroups(array(
+			'userid' => $user,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($group, reset($data['groups']));
+		$this->assertEquals(1, count($data['groups']));
+		OC_Group::deleteGroup($group);
+	}
+
+	function testGetUsersGroupOnOther() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		OC_Group::addToGroup($user2, $group);
+		$result = \OCA\provisioning_API\Users::getUsersGroups(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$data = $result->getData();
+		OC_Group::deleteGroup($group);
+	}
+
+	function testGetUsersGroupOnOtherAsAdmin() {
+		$user1 = $this->generateUsers();
+		OC_Group::addToGroup($user1, 'admin');
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		OC_Group::addToGroup($user2, $group);
+		$result = \OCA\provisioning_API\Users::getUsersGroups(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($group, reset($data['groups']));
+		$this->assertEquals(1, count($data['groups']));
+		OC_Group::deleteGroup($group);
+	}
+
+	function testGetUsersGroupsOnOtherAsSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		$group2 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::createGroup($group2);
+		OC_Group::addToGroup($user2, $group1);
+		OC_Group::addToGroup($user2, $group2);
+		OC_Group::addToGroup($user1, $group1);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$result = \OCA\provisioning_API\Users::getUsersGroups(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($group1, reset($data['groups']));
+		$this->assertEquals(1, count($data['groups']));
+		OC_Group::deleteGroup($group1);
+		OC_Group::deleteGroup($group2);
+	}
+
+	function testGetUsersGroupsOnOtherAsIrelevantSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		$group2 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::createGroup($group2);
+		OC_Group::addToGroup($user2, $group2);
+		OC_Group::addToGroup($user1, $group1);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$result = \OCA\provisioning_API\Users::getUsersGroups(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		OC_Group::deleteGroup($group1);
+		OC_Group::deleteGroup($group2);
+	}
+
+	function testAddToGroup() {
+		$user = $this->generateUsers();
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		OC_User::setUserId($user);
+		$_POST['groupid'] = $group;
+		$result = \OCA\provisioning_API\Users::addToGroup(array(
+			'userid' => $user,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertFalse(OC_Group::inGroup($user, $group));
+		OC_Group::deleteGroup($group);
+	}
+
+	function testAddToGroupAsAdmin() {
+		$user = $this->generateUsers();
+		OC_Group::addToGroup($user, 'admin');
+		$group = uniqid();
+		OC_Group::createGroup($group);
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user);
+		$_POST['groupid'] = $group;
+		$result = \OCA\provisioning_API\Users::addToGroup(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertTrue(OC_Group::inGroup($user2, $group));
+		OC_Group::deleteGroup($group);
+	}
+
+	function testAddToGroupAsSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$_POST['groupid'] = $group1;
+		$result = \OCA\provisioning_API\Users::addToGroup(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertFalse(OC_Group::inGroup($user2, $group1));
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testAddToGroupAsIrelevantSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		$group2 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::createGroup($group2);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$_POST['groupid'] = $group2;
+		$result = \OCA\provisioning_API\Users::addToGroup(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertFalse(OC_Group::inGroup($user2, $group2));
+		OC_Group::deleteGroup($group1);
+		OC_Group::deleteGroup($group2);
+	}
+
+	// test delete /cloud/users/{userid}/groups
+	function testRemoveFromGroupAsSelf() {
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::addToGroup($user1, $group1);
+		$result = \OCA\provisioning_api\Users::removeFromGroup(array(
+			'userid' => $user1,
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertTrue(OC_Group::inGroup($user1, $group1));
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testRemoveFromGroupAsAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::addToGroup($user2, $group1);
+		OC_Group::addToGroup($user1, 'admin');
+		$result = \OCA\provisioning_api\Users::removeFromGroup(array(
+			'userid' => $user2,
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertFalse(OC_Group::inGroup($user2, $group1));
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testRemoveFromGroupAsSubAdmin() {
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$user2 = $this->generateUsers();
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::addToGroup($user1, $group1);
+		OC_Group::addToGroup($user2, $group1);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$result = \OCA\provisioning_api\Users::removeFromGroup(array(
+			'userid' => $user2,
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertFalse(OC_Group::inGroup($user2, $group1));
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testRemoveFromGroupAsIrelevantSubAdmin() {
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		$user2 = $this->generateUsers();
+		$group1 = uniqid();
+		$group2 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_Group::createGroup($group2);
+		OC_Group::addToGroup($user1, $group1);
+		OC_Group::addToGroup($user2, $group2);
+		OC_SubAdmin::createSubAdmin($user1, $group1);
+		$result = \OCA\provisioning_api\Users::removeFromGroup(array(
+			'userid' => $user2,
+			'_delete' => array(
+				'groupid' => $group2,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertTrue(OC_Group::inGroup($user2, $group2));
+		OC_Group::deleteGroup($group1);
+		OC_Group::deleteGroup($group2);
+	}
+
+	function testCreateSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		$_POST['groupid'] = $group1;
+		$result = \OCA\provisioning_api\Users::addSubAdmin(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertTrue(OC_SubAdmin::isSubAdminofGroup($user2, $group1));
+		OC_Group::deleteGroup($group1);
+
+		$this->resetParams();
+
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$_POST['groupid'] = 'admin';
+		$result = \OCA\provisioning_api\Users::addSubAdmin(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertEquals(103, $result->getStatusCode());
+		$this->assertFalse($result->succeeded());
+
+		$this->resetParams();
+
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		$_POST['groupid'] = $group1;
+		$result = \OCA\provisioning_api\Users::addSubAdmin(array(
+			'userid' => uniqid(),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(101, $result->getStatusCode());
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testRemoveSubAdmin() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_SubAdmin::createSubAdmin($user2, $group1);
+		$result = \OCA\provisioning_api\Users::removeSubAdmin(array(
+			'userid' => $user2,
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$this->assertTrue(!OC_SubAdmin::isSubAdminofGroup($user2, $group1));
+		OC_Group::deleteGroup($group1);
+
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$result = \OCA\provisioning_api\Users::removeSubAdmin(array(
+			'userid' => uniqid(),
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertEquals(101, $result->getStatusCode());
+		$this->assertFalse($result->succeeded());
+
+		$this->resetParams();
+
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		$_POST['groupid'] = $group1;
+		$result = \OCA\provisioning_api\Users::removeSubAdmin(array(
+			'userid' => $user2,
+			'_delete' => array(
+				'groupid' => $group1,
+				),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(102, $result->getStatusCode());
+		OC_Group::deleteGroup($group1);
+	}
+
+	function testGetSubAdminGroups() {
+		$user1 = $this->generateUsers();
+		$user2 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		OC_Group::createGroup($group1);
+		OC_SubAdmin::createSubAdmin($user2, $group1);
+		$result = \OCA\provisioning_api\Users::getUserSubAdminGroups(array(
+			'userid' => $user2,
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertTrue($result->succeeded());
+		$data = $result->getData();
+		$this->assertEquals($group1, reset($data));
+		OC_Group::deleteGroup($group1);
+
+		$user1 = $this->generateUsers();
+		OC_User::setUserId($user1);
+		OC_Group::addToGroup($user1, 'admin');
+		$group1 = uniqid();
+		$result = \OCA\provisioning_api\Users::getUserSubAdminGroups(array(
+			'userid' => uniqid(),
+			));
+		$this->assertInstanceOf('OC_OCS_Result', $result);
+		$this->assertFalse($result->succeeded());
+		$this->assertEquals(101, $result->getStatusCode());
+	}
+
+	// Remove all the temporary users
+	function tearDown() {
+		foreach($this->users as $user) {
+			\OC_User::deleteUser($user);
+		}
+	}
+
+}
diff --git a/tests/enable_all.php b/tests/enable_all.php
index d6c3184edd609402deff85e35e35a2dbdced5c88..e9b538713a5b67fd0a076150ef42320d01709714 100644
--- a/tests/enable_all.php
+++ b/tests/enable_all.php
@@ -21,4 +21,5 @@ enableApp('files_trashbin');
 enableApp('files_encryption');
 enableApp('user_ldap');
 enableApp('files_versions');
+enableApp('provisioning_api');
 
diff --git a/tests/phpunit-autotest.xml b/tests/phpunit-autotest.xml
index 15e0e3dd40802bb2b6c29d9226b582441a60065e..18d40d86ef715e65164deee16f321b9eff5b4bb1 100644
--- a/tests/phpunit-autotest.xml
+++ b/tests/phpunit-autotest.xml
@@ -28,6 +28,8 @@
 				<directory suffix=".php">../apps/files_trashbin/l10n</directory>
 				<directory suffix=".php">../apps/user_ldap/l10n</directory>
 				<directory suffix=".php">../apps/user_webdavauth/l10n</directory>
+				<directory suffix=".php">../provisioning_api/l10n</directory>
+				<directory suffix=".php">../provisioning_api/tests</directory>
 				<directory suffix=".php">../lib/MDB2</directory>
 				<directory suffix=".php">../lib/l10n</directory>
 				<directory suffix=".php">../core/l10n</directory>
diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist
index 95abe47396554838ef9a778dbdb64500d2f51569..c1d02dd32a047216e724fab27843c823a28cf32c 100644
--- a/tests/phpunit.xml.dist
+++ b/tests/phpunit.xml.dist
@@ -23,6 +23,8 @@
 				<directory suffix=".php">../apps/files_trashbin/l10n</directory>
 				<directory suffix=".php">../apps/user_ldap/l10n</directory>
 				<directory suffix=".php">../apps/user_webdavauth/l10n</directory>
+				<directory suffix=".php">../provisioning_api/l10n</directory>
+				<directory suffix=".php">../provisioning_api/tests</directory>
 				<directory suffix=".php">../lib/MDB2</directory>
 				<directory suffix=".php">../lib/l10n</directory>
 				<directory suffix=".php">../core/l10n</directory>