diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
new file mode 100644
index 0000000..93f7bd3
--- /dev/null
+++ b/lib/AppInfo/Application.php
@@ -0,0 +1,45 @@
+
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace OCA\UserManagement\AppInfo;
+
+
+use OCA\UserManagement\SubadminMiddleware;
+use OCP\AppFramework\App;
+use OCP\IContainer;
+
+class Application extends App {
+
+ function __construct(array $urlParams = []) {
+ parent::__construct('user_management', $urlParams);
+
+ /**
+ * Middleware
+ */
+ $this->getContainer()
+ ->registerService('SubadminMiddleware', function(IContainer $c){
+ return $c->query(SubadminMiddleware::class);
+ });
+ // Execute middlewares
+ $this->getContainer()->registerMiddleWare('SubadminMiddleware');
+ }
+
+}
\ No newline at end of file
diff --git a/lib/SubadminMiddleware.php b/lib/SubadminMiddleware.php
new file mode 100644
index 0000000..15f192c
--- /dev/null
+++ b/lib/SubadminMiddleware.php
@@ -0,0 +1,104 @@
+
+ * @author Morris Jobke
+ * @author Roeland Jago Douma
+ * @author Thomas Müller
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace OCA\UserManagement;
+
+use OC\AppFramework\Http;
+use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
+use OC\AppFramework\Utility\ControllerMethodReflector;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Middleware;
+use OCP\IGroupManager;
+use OCP\IUserSession;
+
+/**
+ * Verifies whether an user has at least subadmin rights.
+ * To bypass use the `@NoSubadminRequired` annotation
+ *
+ * @package OC\Settings\Middleware
+ */
+class SubadminMiddleware extends Middleware {
+ /** @var IUserSession */
+ private $userSession;
+ /** @var IGroupManager */
+ private $groupManager;
+ /** @var ControllerMethodReflector */
+ protected $reflector;
+
+ /**
+ * @param ControllerMethodReflector $reflector
+ * @param IGroupManager $groupManager
+ * @param IUserSession $userSession
+ */
+ public function __construct(ControllerMethodReflector $reflector,
+ IGroupManager $groupManager,
+ IUserSession $userSession) {
+ $this->reflector = $reflector;
+ $this->groupManager = $groupManager;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * 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')) {
+ // Check if current user (active and not in incognito mode)
+ // can manage users
+ $hasUserManagementPrivileges = false;
+ $activeUser = $this->userSession->getUser();
+ if($activeUser !== null) {
+ //Admin and SubAdmins are allowed to access user management
+ $hasUserManagementPrivileges = $this->groupManager->isAdmin($activeUser->getUID())
+ || $this->groupManager->getSubAdmin()->isSubAdmin($activeUser);
+ }
+
+ if(!$hasUserManagementPrivileges) {
+ throw new NotAdminException('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
+ * @throws \Exception
+ */
+ public function afterException($controller, $methodName, \Exception $exception) {
+ if($exception instanceof NotAdminException) {
+ $response = new TemplateResponse('core', '403', [], 'guest');
+ $response->setStatus(Http::STATUS_FORBIDDEN);
+ return $response;
+ }
+
+ throw $exception;
+ }
+
+}
diff --git a/tests/unit/SubadminMiddlewareTest.php b/tests/unit/SubadminMiddlewareTest.php
new file mode 100644
index 0000000..f115988
--- /dev/null
+++ b/tests/unit/SubadminMiddlewareTest.php
@@ -0,0 +1,145 @@
+groupManager = $this->getMockBuilder(IGroupManager::class)
+ ->disableOriginalConstructor()->getMock();
+
+ $this->subadminManager = $this->getMockBuilder(ISubAdminManager::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->groupManager->expects($this->any())
+ ->method('getSubAdmin')
+ ->will($this->returnValue($this->subadminManager));
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->will($this->returnValue(false));
+
+ $this->session = $this->getMockBuilder(IUserSession::class)
+ ->disableOriginalConstructor()->getMock();
+ $user = $this->getMockBuilder('\OC\User\User')
+ ->disableOriginalConstructor()->getMock();
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('foo'));
+ $this->session
+ ->expects($this->any())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->reflector = $this->getMockBuilder(ControllerMethodReflector::class)
+ ->disableOriginalConstructor()->getMock();
+ $this->controller = $this->getMockBuilder(Controller::class)
+ ->disableOriginalConstructor()->getMock();
+
+ $this->subadminMiddleware = new SubadminMiddleware($this->reflector, $this->groupManager, $this->session);
+ }
+
+ /**
+ * @expectedException \OC\AppFramework\Middleware\Security\Exceptions\NotAdminException
+ */
+ public function testBeforeControllerAsUserWithExemption() {
+ $this->reflector
+ ->expects($this->once())
+ ->method('hasAnnotation')
+ ->with('NoSubadminRequired')
+ ->will($this->returnValue(false));
+
+ $this->subadminManager->expects($this->any())
+ ->method('isSubAdmin')
+ ->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->subadminManager->expects($this->any())
+ ->method('isSubAdmin')
+ ->will($this->returnValue(false));
+ $this->subadminMiddleware->beforeController($this->controller, 'foo');
+ }
+
+ public function testBeforeControllerAsSubAdminWithoutExemption() {
+ $this->reflector
+ ->expects($this->once())
+ ->method('hasAnnotation')
+ ->with('NoSubadminRequired')
+ ->will($this->returnValue(false));
+
+ $this->subadminManager->expects($this->any())
+ ->method('isSubAdmin')
+ ->will($this->returnValue(true));
+ $this->subadminMiddleware->beforeController($this->controller, 'foo');
+ }
+
+ public function testBeforeControllerAsSubAdminWithExemption() {
+ $this->reflector
+ ->expects($this->once())
+ ->method('hasAnnotation')
+ ->with('NoSubadminRequired')
+ ->will($this->returnValue(true));
+
+ $this->subadminManager->expects($this->any())
+ ->method('isSubAdmin')
+ ->will($this->returnValue(true));
+ $this->subadminMiddleware->beforeController($this->controller, 'foo');
+ }
+
+ public function testAfterNotAdminException() {
+ $expectedResponse = new TemplateResponse('core', '403', [], 'guest');
+ $expectedResponse->setStatus(403);
+ $this->assertEquals($expectedResponse, $this->subadminMiddleware->afterException($this->controller, 'foo', new NotAdminException()));
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function testAfterRegularException() {
+ $expectedResponse = new TemplateResponse('core', '403', [], 'guest');
+ $expectedResponse->setStatus(403);
+ $this->subadminMiddleware->afterException($this->controller, 'foo', new \Exception());
+ }
+}