diff --git a/appinfo/Migrations/Version20161209151129.php b/appinfo/Migrations/Version20161209151129.php
index 58393699..be20b74b 100644
--- a/appinfo/Migrations/Version20161209151129.php
+++ b/appinfo/Migrations/Version20161209151129.php
@@ -2,29 +2,29 @@
namespace OCA\CustomGroups\Migrations;
-use Doctrine\DBAL\Migrations\AbstractMigration;
+use OCP\Migration\ISchemaMigration;
use Doctrine\DBAL\Schema\Schema;
/**
- * Auto-generated Migration: Please modify to your needs!
+ * Create initial tables for the customgroups app
*/
-class Version20161209151129 extends AbstractMigration {
+class Version20161209151129 implements ISchemaMigration {
/**
* @param Schema $schema
*/
- public function up(Schema $schema) {
- $this->createGroupsTable($schema);
- $this->createMembersTable($schema);
+ public function changeSchema(Schema $schema, array $options) {
+ $prefix = $options['tablePrefix'];
+ $this->createGroupsTable($prefix, $schema);
+ $this->createMembersTable($prefix, $schema);
}
- private function createGroupsTable(Schema $schema) {
- $prefix = $this->connection->getPrefix();
+ private function createGroupsTable($prefix, Schema $schema) {
$table = $schema->createTable("${prefix}custom_group");
- $table->addColumn('group_id', 'integer', [
+ $table->addColumn('group_id', 'bigint', [
'autoincrement' => true,
'unsigned' => true,
'notnull' => true,
- 'length' => 4,
+ 'length' => 20,
]);
$table->addColumn('uri', 'string', [
'length' => 255,
@@ -39,19 +39,18 @@ private function createGroupsTable(Schema $schema) {
$table->setPrimaryKey(['group_id']);
}
- private function createMembersTable(Schema $schema) {
- $prefix = $this->connection->getPrefix();
+ private function createMembersTable($prefix, Schema $schema) {
$table = $schema->createTable("${prefix}custom_group_member");
- $table->addColumn('group_id', 'integer', [
+ $table->addColumn('group_id', 'bigint', [
'unsigned' => true,
'notnull' => true,
- 'length' => 4,
+ 'length' => 20,
]);
$table->addColumn('user_id', 'string', [
'length' => 64,
'notnull' => true,
]);
- $table->addColumn('is_admin', 'integer', [
+ $table->addColumn('role', 'integer', [
'length' => 4,
'notnull' => true,
'default' => 0,
diff --git a/appinfo/info.xml b/appinfo/info.xml
index e0f7e00b..b27c79f2 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,6 +16,14 @@
true
-
+
+
+
+ OCA\CustomGroups\Dav\CustomGroupsPlugin
+
+
+ OCA\CustomGroups\Dav\RootCollection
+
+
diff --git a/lib/Application.php b/lib/Application.php
index 493af54d..9520d2d9 100644
--- a/lib/Application.php
+++ b/lib/Application.php
@@ -2,7 +2,7 @@
/**
* @author Vincent Petry
*
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
diff --git a/lib/CustomGroupsBackend.php b/lib/CustomGroupsBackend.php
index 7275cdab..41dd1569 100644
--- a/lib/CustomGroupsBackend.php
+++ b/lib/CustomGroupsBackend.php
@@ -2,7 +2,7 @@
/**
* @author Vincent Petry
*
- * @copyright Copyright (c) 2016, ownCloud GmbH.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
@@ -29,10 +29,17 @@ class CustomGroupsBackend implements \OCP\GroupInterface {
const GROUP_ID_PREFIX = 'customgroup_';
/**
+ * Custom groups handler
+ *
* @var CustomGroupsDatabaseHandler
*/
private $handler;
+ /**
+ * Constructor
+ *
+ * @param CustomGroupsDatabaseHandler $handler custom groups handler
+ */
public function __construct(
CustomGroupsDatabaseHandler $handler
) {
@@ -54,7 +61,7 @@ public function implementsActions($actions) {
*
* @param string $uid uid of the user
* @param string $gid gid of the group
- * @return bool
+ * @return boolean true if user is in group, false otherwise
*/
public function inGroup($uid, $gid) {
$numericGroupId = $this->extractNumericGroupId($gid);
@@ -72,10 +79,10 @@ public function inGroup($uid, $gid) {
* @return array an array of group names
*/
public function getUserGroups($uid) {
- $groups = $this->handler->getUserGroups($uid);
- return array_map(function($numericGroupId) {
- return $this->formatGroupId($numericGroupId);
- }, $groups);
+ $memberInfos = $this->handler->getUserMemberships($uid, null);
+ return array_map(function ($memberInfo) {
+ return $this->formatGroupId($memberInfo['group_id']);
+ }, $memberInfos);
}
/**
@@ -87,11 +94,10 @@ public function getUserGroups($uid) {
* @param int $limit limit or -1 to disable
* @param int $offset offset
* @return array an array of group names
- *
*/
public function getGroups($search = '', $limit = -1, $offset = 0) {
$groups = $this->handler->searchGroups($search, $limit, $offset);
- return array_map(function($groupInfo) {
+ return array_map(function ($groupInfo) {
return $this->formatGroupId($groupInfo['group_id']);
}, $groups);
}
@@ -129,13 +135,13 @@ public function getGroupDetails($gid) {
}
/**
- * Returns a list of all users in a group
+ * Not supported, returns an empty array.
*
- * @param string $gid
- * @param string $search
- * @param int $limit
- * @param int $offset
- * @return array an array of user ids
+ * @param string $gid group id
+ * @param string $search search string
+ * @param int $limit limit
+ * @param int $offset offset
+ * @return array empty array
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
// not exposed to regular user management
diff --git a/lib/CustomGroupsDatabaseHandler.php b/lib/CustomGroupsDatabaseHandler.php
index f237b314..7a7745e7 100644
--- a/lib/CustomGroupsDatabaseHandler.php
+++ b/lib/CustomGroupsDatabaseHandler.php
@@ -2,7 +2,7 @@
/**
* @author Vincent Petry
*
- * @copyright Copyright (c) 2016, ownCloud GmbH.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
@@ -29,17 +29,29 @@
*/
class CustomGroupsDatabaseHandler {
+ const ROLE_MEMBER = 0;
+ const ROLE_ADMIN = 1;
+
/**
+ * Database connection
+ *
* @var IDBConnection
- */
+ */
private $dbConn;
/**
+ * Logger
+ *
* @var ILogger
*/
private $logger;
-
+ /**
+ * Constructor
+ *
+ * @param IDBConnection $dbConn database connection
+ * @param ILogger $logger logger
+ */
public function __construct(IDBConnection $dbConn, ILogger $logger) {
$this->dbConn = $dbConn;
$this->logger = $logger;
@@ -50,7 +62,7 @@ public function __construct(IDBConnection $dbConn, ILogger $logger) {
*
* @param string $uid uid of the user
* @param int $numericGroupId id of the group
- * @return bool
+ * @return boolean true if the user is in group, false otherwise
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
public function inGroup($uid, $numericGroupId) {
@@ -69,27 +81,38 @@ public function inGroup($uid, $numericGroupId) {
}
/**
- * Get all groups a user belongs to
+ * Get all group memberships of the given user
*
* @param string $uid Name of the user
- * @return array an array of numeric group ids
+ * @param null|int $roleFilter optional role filter
+ * @return array an array of member info
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
- public function getUserGroups($uid) {
+ public function getUserMemberships($uid, $roleFilter = null) {
$qb = $this->dbConn->getQueryBuilder();
- $cursor = $qb->select('group_id')
- ->from('custom_group_member')
- ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)))
- ->orderBy('group_id', 'ASC')
- ->execute();
+ $qb->select('m.group_id', 'm.user_id', 'm.role', 'g.uri', 'g.display_name')
+ ->from('custom_group_member', 'm')
+ ->from('custom_group', 'g')
+ ->where($qb->expr()->eq('g.group_id', 'm.group_id'))
+ ->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)))
+ ->orderBy('m.group_id', 'ASC');
+
+ if (!is_null($roleFilter)) {
+ $qb->andWhere($qb->expr()->eq('role', $qb->createNamedParameter($roleFilter)));
+ }
- $groups = [];
+ $cursor = $qb->execute();
+
+ $results = [];
while ($row = $cursor->fetch()) {
- $groups[] = (int)$row['group_id'];
+ $result = $this->formatMemberInfo($row);
+ $result['uri'] = $row['uri'];
+ $result['display_name'] = $row['display_name'];
+ $results[] = $result;
}
$cursor->closeCursor();
- return $groups;
+ return $results;
}
/**
@@ -153,7 +176,8 @@ public function getGroupByUri($uri) {
/**
* Returns the info for a given group.
*
- * @param string $gid group id
+ * @param string $field field to filter by
+ * @param string $numericGroupId numeric group id
* @return array|null group info or null if not found
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
@@ -186,6 +210,7 @@ public function getGroups() {
/**
* Creates a new group
*
+ * @param string $uri group URI
* @param string $displayName display name
* @return int group id
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
@@ -197,7 +222,10 @@ public function createGroup($uri, $displayName = null) {
'display_name' => $displayName,
]);
} catch (\Doctrine\DBAL\Exception\UniqueConstraintViolationException $e) {
- $this->logger->logException($e, ['app' => 'customgroups', 'message' => 'Cannot create a group that already exists']);
+ $this->logger->logException($e, [
+ 'app' => 'customgroups',
+ 'message' => 'Cannot create a group that already exists'
+ ]);
return null;
}
@@ -233,19 +261,40 @@ public function deleteGroup($gid) {
return ($result === 1);
}
+ /**
+ * Update group info
+ *
+ * @param int $gid numeric group id
+ * @param int $uri group uri
+ * @param string $displayName group display name
+ * @return bool true if the info got updated, false otherwise
+ * @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
+ */
+ public function updateGroup($gid, $uri, $displayName) {
+ $qb = $this->dbConn->getQueryBuilder();
+ $result = $qb->update('custom_group')
+ ->set('uri', $qb->createNamedParameter($uri))
+ ->set('display_name', $qb->createNamedParameter($displayName))
+ ->where($qb->expr()->eq('group_id', $qb->createNamedParameter($gid)))
+ ->execute();
+
+ return $result === 1;
+ }
+
/**
* Add a user to a group.
*
* @param string $uid user id
* @param int $gid numeric group id
- * @return bool true if user was added, false otherwise
+ * @param int $role initial permission
+ * @return boolean true if user was added, false otherwise
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
- public function addToGroup($uid, $gid, $isAdmin = false) {
+ public function addToGroup($uid, $gid, $role = self::ROLE_MEMBER) {
$result = $this->dbConn->insertIfNotExist('*PREFIX*custom_group_member', [
'user_id' => $uid,
'group_id' => $gid,
- 'is_admin' => $isAdmin ? 1 : 0
+ 'role' => $role
]);
return ($result === 1);
@@ -273,16 +322,23 @@ public function removeFromGroup($uid, $gid) {
* Returns the group members
*
* @param int $gid numeric group id
+ * @param null|bool $roleFilter optional role filter, set to true or false to
+ * filter by non-admin-only or admin-only
* @return array array of member info
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
- public function getGroupMembers($gid) {
+ public function getGroupMembers($gid, $roleFilter = null) {
$qb = $this->dbConn->getQueryBuilder();
- $cursor = $qb->select(['user_id', 'group_id', 'is_admin'])
+ $qb->select(['user_id', 'group_id', 'role'])
->from('custom_group_member')
->where($qb->expr()->eq('group_id', $qb->createNamedParameter($gid)))
- ->orderBy('user_id', 'ASC')
- ->execute();
+ ->orderBy('user_id', 'ASC');
+
+ if (!is_null($roleFilter)) {
+ $qb->andWhere($qb->expr()->eq('role', $qb->createNamedParameter($roleFilter)));
+ }
+
+ $cursor = $qb->execute();
$results = [];
while ($row = $cursor->fetch()) {
@@ -303,7 +359,7 @@ public function getGroupMembers($gid) {
*/
public function getGroupMemberInfo($gid, $uid) {
$qb = $this->dbConn->getQueryBuilder();
- $cursor = $qb->select(['user_id', 'group_id', 'is_admin'])
+ $cursor = $qb->select(['user_id', 'group_id', 'role'])
->from('custom_group_member')
->where($qb->expr()->eq('group_id', $qb->createNamedParameter($gid)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)))
@@ -325,14 +381,14 @@ public function getGroupMemberInfo($gid, $uid) {
*
* @param int $gid numeric group id
* @param int $uid user id
- * @param bool $isAdmin whether the member is a group admin
+ * @param int $role membership role
* @return bool true if the info got updated, false otherwise
* @throws \Doctrine\DBAL\Exception\DriverException in case of database exception
*/
- public function setGroupMemberInfo($gid, $uid, $isAdmin) {
+ public function setGroupMemberInfo($gid, $uid, $role) {
$qb = $this->dbConn->getQueryBuilder();
$result = $qb->update('custom_group_member')
- ->set('is_admin', $qb->createNamedParameter($isAdmin ? 1 : 0))
+ ->set('role', $qb->createNamedParameter($role))
->where($qb->expr()->eq('group_id', $qb->createNamedParameter($gid)))
->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($uid)))
->execute();
@@ -346,11 +402,11 @@ public function setGroupMemberInfo($gid, $uid, $isAdmin) {
* @param array $row database row
* @return array formatted array
*/
- private function formatMemberInfo($row) {
+ private function formatMemberInfo(array $row) {
return [
'user_id' => $row['user_id'],
- 'group_id' => $row['group_id'],
- 'is_admin' => (int)$row['is_admin'] !== 0,
+ 'group_id' => (int)$row['group_id'],
+ 'role' => (int)$row['role'],
];
}
}
diff --git a/lib/Dav/CustomGroupsPlugin.php b/lib/Dav/CustomGroupsPlugin.php
new file mode 100644
index 00000000..61f72a24
--- /dev/null
+++ b/lib/Dav/CustomGroupsPlugin.php
@@ -0,0 +1,195 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserSession;
+use OCA\CustomGroups\Dav\RootCollection;
+use OCA\CustomGroups\Dav\MembershipNode;
+use OCA\CustomGroups\Dav\GroupsCollection;
+use Sabre\DAV\ServerPlugin;
+use OCA\CustomGroups\Dav\CustomGroupMemberNode;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\DAV\Xml\Element\Response;
+use Sabre\DAV\Xml\Response\MultiStatus;
+/**
+ * Sabre plugin to handle custom groups
+ */
+class CustomGroupsPlugin extends ServerPlugin {
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ const REPORT_NAME = '{http://owncloud.org/ns}filter-members';
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ protected $groupsHandler;
+
+ /**
+ * Sabre server
+ *
+ * @var \Sabre\DAV\Server $server
+ */
+ private $server;
+
+ /**
+ * User session
+ *
+ * @var \OCP\IUserSession
+ */
+ protected $userSession;
+
+ /**
+ * Custom groups plugin
+ *
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param IUserSession $userSession user session
+ */
+ public function __construct(CustomGroupsDatabaseHandler $groupsHandler, IUserSession $userSession) {
+ $this->groupsHandler = $groupsHandler;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server Sabre server
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+
+ $uri = $this->server->getRequestUri();
+ $uri = '/' . trim($uri) . '/';
+ if (strpos($uri, '/customgroups/') === false) {
+ return;
+ }
+
+ if ($this->userSession === null || $this->userSession->getUser() === null) {
+ return;
+ }
+
+ $this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
+ $ns = '{' . self::NS_OWNCLOUD . '}';
+ $this->server->resourceTypeMapping[MembershipNode::class] = $ns . 'customgroups-membership';
+ $this->server->resourceTypeMapping[GroupsCollection::class] = $ns . 'customgroups-group';
+ $this->server->protectedProperties[] = $ns . 'user-id';
+ $this->server->protectedProperties[] = $ns . 'group-uri';
+
+ $this->server->on('report', [$this, 'onReport']);
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ *
+ * @param string $uri URI
+ * @return array
+ */
+ public function getSupportedReportSet($uri) {
+ $node = $this->server->tree->getNodeForPath($uri);
+ if (!$node instanceof RootCollection) {
+ return [self::REPORT_NAME];
+ }
+ return [];
+ }
+
+ /**
+ * REPORT operations to look for comments
+ *
+ * @param string $reportName report name
+ * @param array $report report data
+ * @param string $uri URI
+ * @return bool true if processed
+ * @throws BadRequest if missing properties
+ */
+ public function onReport($reportName, $report, $uri) {
+ $node = $this->server->tree->getNodeForPath($uri);
+ if (!$node instanceof RootCollection || $reportName !== self::REPORT_NAME) {
+ return;
+ }
+
+ $requestedProps = [];
+ $filterRules = [];
+
+ $ns = '{' . self::NS_OWNCLOUD . '}';
+ foreach ($report as $reportProps) {
+ $name = $reportProps['name'];
+ if ($name === $ns . 'filter-rules') {
+ $filterRules = $reportProps['value'];
+ } else if ($name === '{DAV:}prop') {
+ // propfind properties
+ foreach ($reportProps['value'] as $propVal) {
+ $requestedProps[] = $propVal['name'];
+ }
+ }
+ }
+
+ $filterUserId = null;
+ $filterAdminFlag = null;
+ foreach ($filterRules as $filterRule) {
+ if ($filterRule['name'] === $ns . 'user-id') {
+ $filterUserId = $filterRule['value'];
+ } else if ($filterRule['name'] === $ns . 'role') {
+ $filterAdminFlag = $filterRule['value'];
+ }
+ }
+
+ if (is_null($filterUserId)) {
+ // an empty filter would return all existing users which would be useless
+ throw new BadRequest('Missing user-id property');
+ }
+
+ $memberInfos = $this->groupsHandler->getUserMemberships($filterUserId, $filterAdminFlag);
+
+ $responses = [];
+ foreach ($memberInfos as $memberInfo) {
+ $node = new CustomGroupMemberNode($memberInfo, $this->groupsHandler, $this->userSession);
+ $uri = $memberInfo['uri'];
+ $nodePath = $this->server->getRequestUri() . '/' . $uri . '/' . $node->getName();
+ $resultSet = $node->getProperties($requestedProps);
+ $responses[] = new Response(
+ $this->server->getBaseUri() . $nodePath,
+ [200 => $resultSet],
+ 200
+ );
+ }
+
+ $xml = $this->server->xml->write(
+ '{DAV:}multistatus',
+ new MultiStatus($responses)
+ );
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setBody($xml);
+
+ return false;
+ }
+}
diff --git a/lib/Dav/GroupMembershipCollection.php b/lib/Dav/GroupMembershipCollection.php
new file mode 100644
index 00000000..f1294f94
--- /dev/null
+++ b/lib/Dav/GroupMembershipCollection.php
@@ -0,0 +1,268 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\PreconditionFailed;
+
+/**
+ * Group memberships collection for a given group
+ */
+class GroupMembershipCollection implements \Sabre\DAV\ICollection, \Sabre\DAV\IProperties {
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ const PROPERTY_DISPLAY_NAME = '{http://owncloud.org/ns}display-name';
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * Membership helper
+ *
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * Group information
+ *
+ * @var array
+ */
+ private $groupInfo;
+
+ /**
+ * Constructor
+ *
+ * @param array $groupInfo group info
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param MembershipHelper $helper membership helper
+ */
+ public function __construct(
+ array $groupInfo,
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->groupInfo = $groupInfo;
+ $this->helper = $helper;
+ }
+
+ /**
+ * Deletes the group
+ *
+ * @throws Forbidden if no permisson to delete this group
+ */
+ public function delete() {
+ $groupId = $this->groupInfo['group_id'];
+ if (!$this->helper->isUserAdmin($groupId)) {
+ throw new Forbidden("No permission to delete group \"$groupId\"");
+ }
+ $this->groupsHandler->deleteGroup($groupId);
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * @return string
+ */
+ public function getName() {
+ return (string)$this->groupInfo['uri'];
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name the new name
+ * @throws MethodNotAllowed not supported
+ */
+ public function setName($name) {
+ throw new MethodNotAllowed();
+ }
+
+ /**
+ * Returns null
+ *
+ * @return int null
+ */
+ public function getLastModified() {
+ return null;
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * @param PropPatch $propPatch PropPatch request
+ */
+ public function propPatch(PropPatch $propPatch) {
+ $propPatch->handle(self::PROPERTY_DISPLAY_NAME, [$this, 'updateDisplayName']);
+ }
+
+ /**
+ * Returns a list of properties for this node.
+ *
+ * @param array|null $properties requested properties or null for all
+ * @return array property values
+ */
+ public function getProperties($properties) {
+ if ($properties === null || in_array(self::PROPERTY_DISPLAY_NAME, $properties)) {
+ return [
+ self::PROPERTY_DISPLAY_NAME => $this->groupInfo['display_name'],
+ ];
+ }
+ return [];
+ }
+
+ /**
+ * Adds a new member to this group
+ *
+ * @param string $userId user id to add
+ * @param resource|string $data unused
+ * @throws Forbidden if the current user has insufficient permissions
+ * @throws PreconditionFailed if the user did not exist
+ */
+ public function createFile($userId, $data = null) {
+ $groupId = $this->groupInfo['group_id'];
+ if (!$this->helper->isUserAdmin($groupId)) {
+ throw new Forbidden("No permission to add members to group \"$groupId\"");
+ }
+ // check if the user name actually exists
+ $user = $this->helper->getUser($userId);
+ // not existing user or mismatch user casing
+ if (is_null($user) || $userId !== $user->getUID()) {
+ throw new PreconditionFailed("The user \"$userId\" does not exist");
+ }
+
+ if (!$this->groupsHandler->addToGroup($userId, $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER)) {
+ throw new PreconditionFailed("The user \"$userId\" is already member of this group");
+ }
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @throws MethodNotAllowed not supported
+ */
+ public function createDirectory($name) {
+ throw new MethodNotAllowed('Cannot create collections');
+ }
+
+ /**
+ * Returns a membership node
+ *
+ * @param string $userId user id
+ * @return CustomGroupMemberNode membership node
+ * @throws NotFound if the given user has no membership in this group
+ * @throws Forbidden if the current user has insufficient permissions
+ */
+ public function getChild($userId) {
+ $groupId = $this->groupInfo['group_id'];
+ if (!$this->helper->isUserMember($groupId) && !$this->helper->isUserAdmin($groupId)) {
+ throw new Forbidden("No permission to list members of group \"$groupId\"");
+ }
+ $memberInfo = $this->groupsHandler->getGroupMemberInfo($groupId, $userId);
+ if (is_null($memberInfo)) {
+ throw new NotFound(
+ "User with id \"$userId\" is not member of group with uri \"$groupId\""
+ );
+ }
+ return $this->createCustomGroupMemberNode($memberInfo);
+ }
+
+ /**
+ * Returns a list of all memberships
+ *
+ * @return CustomGroupMemberNode[] list of memberships
+ * @throws Forbidden if the current user has insufficient permissions
+ */
+ public function getChildren() {
+ $groupId = $this->groupInfo['group_id'];
+ if (!$this->helper->isUserMember($groupId)
+ && !$this->helper->isUserAdmin($groupId)) {
+ throw new Forbidden("No permission to list members of group \"$groupId\"");
+ }
+ $members = $this->groupsHandler->getGroupMembers($groupId);
+ return array_map(function ($memberInfo) {
+ return $this->createCustomGroupMemberNode($memberInfo);
+ }, $members);
+ }
+
+ /**
+ * Returns whether a user has a membership in this group.
+ *
+ * @param string $userId user id
+ * @return boolean true if the user has a membership, false otherwise
+ * @throws Forbidden if the current user has insufficient permissions
+ */
+ public function childExists($userId) {
+ $groupId = $this->groupInfo['group_id'];
+ if (!$this->helper->isUserMember($groupId)) {
+ throw new Forbidden("No permission to list members of group \"$groupId\"");
+ }
+ return $this->groupsHandler->inGroup($userId, $groupId);
+ }
+
+ /**
+ * Update the display name.
+ * Returns 403 status code if the current user has insufficient permissions.
+ *
+ * @param string $displayName display name to set
+ * @return boolean|int true or status code
+ */
+ public function updateDisplayName($displayName) {
+ if (!$this->helper->isUserAdmin($this->groupInfo['group_id'])) {
+ return 403;
+ }
+
+ $result = $this->groupsHandler->updateGroup(
+ $this->groupInfo['group_id'],
+ $this->groupInfo['uri'],
+ $displayName
+ );
+ $this->groupInfo['display_name'] = $displayName;
+
+ return $result;
+ }
+
+ /**
+ * Creates a membership node based on the given membership info.
+ *
+ * @param array $memberInfo membership info
+ * @return CustomGroupMemberNode membership node
+ */
+ private function createCustomGroupMemberNode(array $memberInfo) {
+ return new MembershipNode(
+ $memberInfo,
+ $memberInfo['user_id'],
+ $this->groupsHandler,
+ $this->helper
+ );
+ }
+}
diff --git a/lib/Dav/GroupsCollection.php b/lib/Dav/GroupsCollection.php
new file mode 100644
index 00000000..97a04478
--- /dev/null
+++ b/lib/Dav/GroupsCollection.php
@@ -0,0 +1,178 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use Sabre\DAV\ICollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\MethodNotAllowed;
+
+/**
+ * Collection of custom groups
+ */
+class GroupsCollection implements ICollection {
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * Membership helper
+ *
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * Constructor
+ *
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param MembershipHelper $helper helper
+ */
+ public function __construct(
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->helper = $helper;
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @param resource|string $data unused
+ * @throws MethodNotAllowed not supported
+ */
+ public function createFile($name, $data = null) {
+ throw new MethodNotAllowed('Cannot create regular nodes');
+ }
+
+ /**
+ * Creates a new custom group
+ *
+ * @param string $name group URI
+ * @throws MethodNotAllowed if the group already exists
+ */
+ public function createDirectory($name) {
+ $groupId = $this->groupsHandler->createGroup($name, $name);
+ if (is_null($groupId)) {
+ throw new MethodNotAllowed("Group with uri \"$name\" already exists");
+ }
+
+ // add current user as admin
+ $this->groupsHandler->addToGroup($this->helper->getUserId(), $groupId, true);
+ }
+
+ /**
+ * Returns the custom group node for the given URI.
+ *
+ * @param string $name group URI
+ * @return GroupMembershipCollection node
+ * @throws NotFound if the requested group does not exist
+ */
+ public function getChild($name) {
+ $group = $this->groupsHandler->getGroupByUri($name);
+ if (is_null($group)) {
+ throw new NotFound("Group with uri \"$name\" not found");
+ }
+ return $this->createMembershipsCollection($group);
+ }
+
+ /**
+ * Returns nodes for all existing custom groups.
+ *
+ * @return GroupMembershipCollection[] custom group nodes
+ */
+ public function getChildren() {
+ $allGroups = $this->groupsHandler->getGroups();
+ return array_map(function ($groupInfo) {
+ return $this->createMembershipsCollection($groupInfo);
+ }, $allGroups);
+ }
+
+ /**
+ * Returns whether a custom group exists.
+ *
+ * @param string $name group URI
+ * @return boolean true if the group exists, false otherwise
+ */
+ public function childExists($name) {
+ return !is_null($this->groupsHandler->getGroupByUri($name));
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws MethodNotAllowed not supported
+ */
+ public function delete() {
+ throw new MethodNotAllowed('Cannot delete this collection');
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string node name
+ */
+ public function getName() {
+ return 'groups';
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @throws MethodNotAllowed not supported
+ */
+ public function setName($name) {
+ throw new MethodNotAllowed('Cannot rename this collection');
+ }
+
+ /**
+ * Returns null
+ *
+ * @return int null
+ */
+ public function getLastModified() {
+ return null;
+ }
+
+ /**
+ * Creates a custom group node for the given group info.
+ *
+ * @param array $groupInfo group info
+ * @return GroupMembershipCollection node
+ */
+ private function createMembershipsCollection(array $groupInfo) {
+ return new GroupMembershipCollection(
+ $groupInfo,
+ $this->groupsHandler,
+ $this->helper
+ );
+ }
+}
diff --git a/lib/Dav/MembershipHelper.php b/lib/Dav/MembershipHelper.php
new file mode 100644
index 00000000..2b375c6c
--- /dev/null
+++ b/lib/Dav/MembershipHelper.php
@@ -0,0 +1,177 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserSession;
+use OCP\IUserManager;
+use OCP\IGroupManager;
+
+/**
+ * Membership helper
+ *
+ * Provides method related to the current user's membership and admin roles.
+ */
+class MembershipHelper {
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * User session
+ *
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * User manager
+ *
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * Group manager
+ *
+ * @var IGroupManager
+ */
+ private $groupsManager;
+
+ /**
+ * Membership info for the currently logged in user
+ *
+ * @var array
+ */
+ private $userMemberInfo = [];
+
+ /**
+ * Membership helper
+ *
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param IUserSession $userSession user session
+ * @param IUserManager $userManager user manager
+ * @param IGroupManager $groupManager group manager
+ */
+ public function __construct(
+ CustomGroupsDatabaseHandler $groupsHandler,
+ IUserSession $userSession,
+ IUserManager $userManager,
+ IGroupManager $groupManager
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->userSession = $userSession;
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ }
+
+ /**
+ * Returns the currently logged in user id
+ *
+ * @return string user id
+ */
+ public function getUserId() {
+ return $this->userSession->getUser()->getUID();
+ }
+
+ /**
+ * Returns the user object for a given user id
+ *
+ * @param string $userId user id
+ * @return IUser|null user object or null if user does not exist
+ */
+ public function getUser($userId) {
+ return $this->userManager->get($userId);
+ }
+
+ /**
+ * Returns membership information for the current user
+ *
+ * @param int $groupId group id
+ * @return array membership information
+ */
+ private function getUserMemberInfo($groupId) {
+ if (!isset($this->userMemberInfo[$groupId])) {
+ $userId = $this->getUserId();
+ $this->userMemberInfo[$groupId] = $this->groupsHandler->getGroupMemberInfo($groupId, $userId);
+ }
+ return $this->userMemberInfo[$groupId];
+ }
+
+ /**
+ * Returns whether the current user can administrate this group
+ *
+ * @param int $groupId group id
+ * @return boolean true if the user can administrate, false otherwise
+ */
+ public function isUserAdmin($groupId) {
+ // ownCloud admin is always admin of any custom group
+ if ($this->isUserSuperAdmin()) {
+ return true;
+ }
+ $memberInfo = $this->getUserMemberInfo($groupId);
+ return (!is_null($memberInfo) && $memberInfo['role']);
+ }
+
+ /**
+ * Returns whether the current user is an ownCloud admin
+ *
+ * @return boolean true if the user is an ownCloud admin, false otherwise
+ */
+ public function isUserSuperAdmin() {
+ return ($this->groupManager->isAdmin($this->getUserId()));
+ }
+
+ /**
+ * Returns whether the current user is member of this group
+ *
+ * @param int $groupId group id
+ * @return boolean true if the user is member, false otherwise
+ */
+ public function isUserMember($groupId) {
+ $memberInfo = $this->getUserMemberInfo($groupId);
+ return (!is_null($memberInfo));
+ }
+
+ /**
+ * Returns whether the given group's member is the one and only group admin
+ *
+ * @param int $groupId group id
+ * @param string $userId user id of the admin to check
+ * @return bool true if it's the only admin, false otherwise
+ */
+ public function isTheOnlyAdmin($groupId, $userId) {
+ $groupAdmins = $this->groupsHandler->getGroupMembers($groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
+ if (count($groupAdmins) > 1) {
+ return false;
+ }
+ if ($groupAdmins[0]['user_id'] !== $userId) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/lib/Dav/MembershipNode.php b/lib/Dav/MembershipNode.php
new file mode 100644
index 00000000..ddf833be
--- /dev/null
+++ b/lib/Dav/MembershipNode.php
@@ -0,0 +1,221 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\PreconditionFailed;
+
+/**
+ * Membership node
+ */
+class MembershipNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ const PROPERTY_ROLE = '{http://owncloud.org/ns}role';
+ const PROPERTY_USER_ID = '{http://owncloud.org/ns}user-id';
+ const PROPERTY_GROUP_URI = '{http://owncloud.org/ns}group-uri';
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * Membership information
+ *
+ * @var array
+ */
+ private $memberInfo;
+
+ /**
+ * Membership helper
+ *
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * Membership info for the currently logged in user
+ *
+ * @var array
+ */
+ private $userMemberInfo;
+
+ /**
+ * Node name
+ *
+ * @var string
+ */
+ private $name;
+
+ /**
+ * Constructor
+ *
+ * @param array $memberInfo membership information
+ * @param string $name node name (based on user id or group id)
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param MembershipHelper $helper membership helper
+ */
+ public function __construct(
+ array $memberInfo,
+ $name,
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->name = $name;
+ $this->memberInfo = $memberInfo;
+ $this->helper = $helper;
+ }
+
+ /**
+ * Removes this member from the group
+ *
+ * @throws Forbidden when no permission to delete
+ * @throws PreconditionFailed when membership did not exist
+ */
+ public function delete() {
+ $currentUserId = $this->helper->getUserId();
+ $groupId = $this->memberInfo['group_id'];
+ // admins can remove members
+ // and regular members can remove themselves
+ if (!$this->helper->isUserAdmin($groupId)
+ && !($currentUserId === $this->memberInfo['user_id'] && $this->helper->isUserMember($groupId))
+ ) {
+ throw new Forbidden("No permission to remove members from group \"$groupId\"");
+ }
+
+ // can't remove the last admin
+ if ($this->helper->isTheOnlyAdmin($groupId, $currentUserId)) {
+ throw new Forbidden("Cannot remove the last admin from the group \"$groupId\"");
+ }
+
+ $userId = $this->memberInfo['user_id'];
+ if (!$this->groupsHandler->removeFromGroup(
+ $userId,
+ $groupId
+ )) {
+ // possibly the membership was deleted concurrently
+ throw new PreconditionFailed("Could not remove member \"$userId\" from group \"$groupId\"");
+ };
+ }
+
+ /**
+ * Returns the node name
+ *
+ * @return string node name
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name The new name
+ * @throws MethodNotAllowed not supported
+ */
+ public function setName($name) {
+ throw new MethodNotAllowed();
+ }
+
+ /**
+ * Returns null
+ *
+ * @return int null
+ */
+ public function getLastModified() {
+ return null;
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param PropPatch $propPatch PropPatch query
+ */
+ public function propPatch(PropPatch $propPatch) {
+ $propPatch->handle(self::PROPERTY_ROLE, [$this, 'updateAdminFlag']);
+ }
+
+ /**
+ * Returns a list of properties for this node.
+ *
+ * @param array|null $properties requested properties or null for all
+ * @return array property values
+ */
+ public function getProperties($properties) {
+ $result = [];
+ if ($properties === null || in_array(self::PROPERTY_ROLE, $properties)) {
+ $result[self::PROPERTY_ROLE] = $this->memberInfo['role'];
+ }
+ if ($properties === null || in_array(self::PROPERTY_USER_ID, $properties)) {
+ $result[self::PROPERTY_USER_ID] = $this->memberInfo['user_id'];
+ }
+ if ($properties === null || in_array(self::PROPERTY_GROUP_URI, $properties)) {
+ $result[self::PROPERTY_GROUP_URI] = $this->memberInfo['uri'];
+ }
+ return $result;
+ }
+
+ /**
+ * Update the admin flag.
+ * Returns 403 status code if the current user has insufficient permissions
+ * or if the only group admin is trying to remove their own permission.
+ *
+ * @param int $rolePropValue role value
+ * @return boolean|int true or error status code
+ */
+ public function updateAdminFlag($rolePropValue) {
+ $groupId = $this->memberInfo['group_id'];
+ $userId = $this->memberInfo['user_id'];
+ // only the group admin can change permissions
+ if (!$this->helper->isUserAdmin($groupId)) {
+ return 403;
+ }
+
+ // can't remove admin rights from the last admin
+ if ($rolePropValue !== CustomGroupsDatabaseHandler::ROLE_ADMIN && $this->helper->isTheOnlyAdmin($groupId, $userId)) {
+ return 403;
+ }
+
+ $result = $this->groupsHandler->setGroupMemberInfo(
+ $groupId,
+ $userId,
+ ($rolePropValue === CustomGroupsDatabaseHandler::ROLE_ADMIN)
+ );
+ $this->groupInfo['role'] = $rolePropValue;
+
+ return $result;
+ }
+
+}
diff --git a/lib/Dav/RootCollection.php b/lib/Dav/RootCollection.php
new file mode 100644
index 00000000..3702cdb7
--- /dev/null
+++ b/lib/Dav/RootCollection.php
@@ -0,0 +1,54 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\SimpleCollection;
+
+/**
+ * Root collection for the custom groups and members
+ */
+class RootCollection extends SimpleCollection {
+ /**
+ * Constructor
+ *
+ * @param MembershipHelper $helper membership helper
+ */
+ public function __construct(
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $children = [
+ new GroupsCollection(
+ $groupsHandler,
+ $helper
+ ),
+ new UsersCollection(
+ $groupsHandler,
+ $helper
+ ),
+ ];
+ parent::__construct('customgroups', $children);
+ }
+}
diff --git a/lib/Dav/UserMembershipCollection.php b/lib/Dav/UserMembershipCollection.php
new file mode 100644
index 00000000..5a4d2c6c
--- /dev/null
+++ b/lib/Dav/UserMembershipCollection.php
@@ -0,0 +1,192 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\PropPatch;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\PreconditionFailed;
+use OCA\DAV\Connector\Sabre\Principal;
+
+/**
+ * Membership collection for a specific user
+ */
+class UserMembershipCollection extends Principal implements \Sabre\DAV\ICollection {
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * User id
+ *
+ * @var string
+ */
+ private $userId;
+
+ /**
+ * Membership helper
+ *
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * Constructor
+ *
+ * @param string $userId user id
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param MembershipHelper $helper membership helper
+ */
+ public function __construct(
+ $userId,
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->userId = $userId;
+ $this->helper = $helper;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws MethodNotAllowed not supported
+ */
+ public function delete() {
+ throw new MethodNotAllowed('Not supported');
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->userId;
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name the new name
+ * @throws MethodNotAllowed not supported
+ */
+ public function setName($name) {
+ throw new MethodNotAllowed();
+ }
+
+ /**
+ * Returns null
+ *
+ * @return int null
+ */
+ public function getLastModified() {
+ return null;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws MethodNotAllowed not supported
+ */
+ public function createFile($name, $data = null) {
+ throw new MethodNotAllowed('Not supported');
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @throws MethodNotAllowed not supported
+ */
+ public function createDirectory($name) {
+ throw new MethodNotAllowed('Cannot create collections');
+ }
+
+ /**
+ * Returns a group node
+ *
+ * @param string $groupUri group uri
+ * @return GroupCollection group node
+ * @throws NotFound if the user is not member of the given group
+ */
+ public function getChild($groupUri) {
+ $groupInfo = $this->groupsHandler->getGroupByUri($groupUri);
+ if (!is_null($groupInfo)) {
+ $memberInfo = $this->groupsHandler->getGroupMemberInfo($groupInfo['group_id'], $this->userId);
+ if (!is_null($memberInfo)) {
+ return $this->createGroupNode($memberInfo);
+ }
+ }
+
+ throw new NotFound("Group with uri \"$groupUri\" not found.");
+ }
+
+ /**
+ * Returns a list of all memberships
+ *
+ * @return CustomGroupMemberNode[] list of memberships
+ * @throws Forbidden if the current user has insufficient permissions
+ */
+ public function getChildren() {
+ $memberInfos = $this->groupsHandler->getUserMemberships($this->userId);
+ return array_map(function ($memberInfo) {
+ return $this->createGroupNode($memberInfo);
+ }, $memberInfos);
+ }
+
+ /**
+ * Returns whether a user has a membership in the given.
+ *
+ * @param string $groupUri group uri
+ * @return boolean true if the user has a membership, false otherwise
+ */
+ public function childExists($groupUri) {
+ try {
+ $this->getChild($groupUri);
+ } catch (NotFound $e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Creates a membership node based on the given membership info.
+ *
+ * @param array $memberInfo membership info
+ * @return CustomGroupMemberNode membership node
+ */
+ private function createGroupNode(array $memberInfo) {
+ return new MembershipNode(
+ $memberInfo,
+ $memberInfo['uri'],
+ $this->groupsHandler,
+ $this->helper
+ );
+ }
+}
diff --git a/lib/Dav/UsersCollection.php b/lib/Dav/UsersCollection.php
new file mode 100644
index 00000000..80a3fa58
--- /dev/null
+++ b/lib/Dav/UsersCollection.php
@@ -0,0 +1,176 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Dav;
+
+use Sabre\DAV\ICollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\Exception\Forbidden;
+
+/**
+ * Collection of users
+ */
+class UsersCollection implements ICollection {
+
+ /**
+ * Custom groups handler
+ *
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $groupsHandler;
+
+ /**
+ * Membership helper
+ *
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * Constructor
+ *
+ * @param CustomGroupsDatabaseHandler $groupsHandler custom groups handler
+ * @param MembershipHelper $helper
+ */
+ public function __construct(
+ CustomGroupsDatabaseHandler $groupsHandler,
+ MembershipHelper $helper
+ ) {
+ $this->groupsHandler = $groupsHandler;
+ $this->helper = $helper;
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @param resource|string $data unused
+ * @throws MethodNotAllowed not supported
+ */
+ public function createFile($name, $data = null) {
+ throw new MethodNotAllowed('Cannot create regular nodes');
+ }
+
+ /**
+ * Creates a new custom group
+ *
+ * @param string $name group URI
+ * @throws MethodNotAllowed if the group already exists
+ */
+ public function createDirectory($name) {
+ $groupId = $this->groupsHandler->createGroup($name, $name);
+ if (is_null($groupId)) {
+ throw new MethodNotAllowed("Group with uri \"$name\" already exists");
+ }
+
+ // add current user as admin
+ $this->groupsHandler->addToGroup($this->helper->getUserId(), $groupId, true);
+ }
+
+ /**
+ * Returns the given user's memberships
+ *
+ * @param string $name user id
+ * @return CustomGroupMembershipCollection user membership collection
+ * @throws Forbidden if the current user has insufficient permissions
+ */
+ public function getChild($name) {
+ // users can only query their own memberships
+ // but ownCloud admin can query membership of any user
+ if ($name === $this->helper->getUserId() || $this->helper->isUserSuperAdmin()) {
+ return new UserMembershipCollection(
+ $name,
+ $this->groupsHandler,
+ $this->helper
+ );
+ }
+
+ // regular user can only query for self
+ throw new Forbidden('Insufficient permissions');
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws MethodNotAllowed not supported
+ */
+ public function getChildren() {
+ throw new MethodNotAllowed('Not supported');
+ }
+
+ /**
+ * Returns whether a custom group exists.
+ *
+ * @param string $name user id
+ * @return boolean true if the group exists, false otherwise
+ */
+ public function childExists($name) {
+ try {
+ $this->getChild($name);
+ } catch (Forbidden $e) {
+ return false;
+ } catch (NotFound $e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Not supported
+ *
+ * @throws MethodNotAllowed not supported
+ */
+ public function delete() {
+ throw new MethodNotAllowed('Cannot delete this collection');
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string node name
+ */
+ public function getName() {
+ return 'users';
+ }
+
+ /**
+ * Not supported
+ *
+ * @param string $name name
+ * @throws MethodNotAllowed not supported
+ */
+ public function setName($name) {
+ throw new MethodNotAllowed('Cannot rename this collection');
+ }
+
+ /**
+ * Returns null
+ *
+ * @return int null
+ */
+ public function getLastModified() {
+ return null;
+ }
+}
diff --git a/tests/unit/CustomGroupsBackendTest.php b/tests/unit/CustomGroupsBackendTest.php
index 5fcd269d..fe60acb3 100644
--- a/tests/unit/CustomGroupsBackendTest.php
+++ b/tests/unit/CustomGroupsBackendTest.php
@@ -2,7 +2,7 @@
/**
* @author Vincent Petry
*
- * @copyright Copyright (c) 2016, ownCloud GmbH.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
@@ -68,10 +68,10 @@ public function testInGroup() {
public function testGetUserGroups() {
$this->handler->expects($this->any())
- ->method('getUserGroups')
+ ->method('getUserMemberships')
->will($this->returnValueMap([
- ['user1', [1, 2]],
- ['user2', [1, 3]],
+ ['user1', null, [['group_id' => 1], ['group_id' => 2]]],
+ ['user2', null, [['group_id' => 1], ['group_id' => 3]]],
]));
$this->assertEquals(
diff --git a/tests/unit/CustomGroupsDatabaseHandlerTest.php b/tests/unit/CustomGroupsDatabaseHandlerTest.php
index d78e2a2a..c6375fb3 100644
--- a/tests/unit/CustomGroupsDatabaseHandlerTest.php
+++ b/tests/unit/CustomGroupsDatabaseHandlerTest.php
@@ -2,7 +2,7 @@
/**
* @author Vincent Petry
*
- * @copyright Copyright (c) 2016, ownCloud GmbH.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
@@ -82,6 +82,16 @@ public function testDeleteGroup() {
$this->assertFalse($this->handler->deleteGroup($groupId));
}
+ public function testUpdateGroup() {
+ $groupId = $this->handler->createGroup('my_group', 'My Group');
+ $this->assertTrue($this->handler->updateGroup($groupId, 'meine_gruppe', 'Meine Gruppe'));
+
+ $groupInfo = $this->handler->getGroup($groupId);
+
+ $this->assertEquals('meine_gruppe', $groupInfo['uri']);
+ $this->assertEquals('Meine Gruppe', $groupInfo['display_name']);
+ }
+
public function testSearchGroups() {
$group1Id = $this->handler->createGroup('my_group_1', 'My One Group');
$group2Id = $this->handler->createGroup('my_group_2', 'My Group Two');
@@ -174,8 +184,8 @@ public function testGetGroups() {
public function testAddToGroup() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
- $this->assertTrue($this->handler->addToGroup('user2', $groupId, false));
- $this->assertTrue($this->handler->addToGroup('user1', $groupId, true));
+ $this->assertTrue($this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER));
+ $this->assertTrue($this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN));
$members = $this->handler->getGroupMembers($groupId);
@@ -183,23 +193,23 @@ public function testAddToGroup() {
$this->assertEquals('user1', $members[0]['user_id']);
$this->assertEquals($groupId, $members[0]['group_id']);
- $this->assertTrue($members[0]['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_ADMIN, $members[0]['role']);
$this->assertEquals('user2', $members[1]['user_id']);
$this->assertEquals($groupId, $members[1]['group_id']);
- $this->assertFalse($members[1]['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $members[1]['role']);
// add again returns false
- $this->assertFalse($this->handler->addToGroup('user1', $groupId, true));
+ $this->assertFalse($this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN));
}
public function testRemoveFromGroup() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
$groupId2 = $this->handler->createGroup('my_group2', 'My Group Two');
- $this->handler->addToGroup('user2', $groupId, false);
- $this->handler->addToGroup('user1', $groupId, true);
- $this->handler->addToGroup('user2', $groupId2, false);
+ $this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
+ $this->handler->addToGroup('user2', $groupId2, CustomGroupsDatabaseHandler::ROLE_MEMBER);
$this->assertTrue($this->handler->removeFromGroup('user2', $groupId));
@@ -208,7 +218,7 @@ public function testRemoveFromGroup() {
$this->assertEquals('user1', $members[0]['user_id']);
$this->assertEquals($groupId, $members[0]['group_id']);
- $this->assertTrue($members[0]['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_ADMIN, $members[0]['role']);
// member still exists in the other group
$members2 = $this->handler->getGroupMembers($groupId2);
@@ -216,17 +226,38 @@ public function testRemoveFromGroup() {
$this->assertCount(1, $members2);
$this->assertEquals('user2', $members2[0]['user_id']);
$this->assertEquals($groupId2, $members2[0]['group_id']);
- $this->assertFalse($members2[0]['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $members2[0]['role']);
// remove again returns false
$this->assertFalse($this->handler->removeFromGroup('user2', $groupId));
}
+ public function testGetGroupMembersFilter() {
+ $groupId = $this->handler->createGroup('my_group', 'My Group');
+
+ $this->assertTrue($this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER));
+ $this->assertTrue($this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN));
+
+ $adminMembers = $this->handler->getGroupMembers($groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
+ $nonAdminMembers = $this->handler->getGroupMembers($groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+
+ $this->assertCount(1, $adminMembers);
+ $this->assertCount(1, $nonAdminMembers);
+
+ $this->assertEquals('user1', $adminMembers[0]['user_id']);
+ $this->assertEquals($groupId, $adminMembers[0]['group_id']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_ADMIN, $adminMembers[0]['role']);
+
+ $this->assertEquals('user2', $nonAdminMembers[0]['user_id']);
+ $this->assertEquals($groupId, $nonAdminMembers[0]['group_id']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $nonAdminMembers[0]['role']);
+ }
+
public function testDeleteRemovesMembers() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
- $this->handler->addToGroup('user2', $groupId, false);
- $this->handler->addToGroup('user1', $groupId, true);
+ $this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
$this->assertTrue($this->handler->deleteGroup($groupId));
@@ -237,54 +268,77 @@ public function testDeleteRemovesMembers() {
public function testGetGroupMemberInfo() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
- $this->handler->addToGroup('user2', $groupId, false);
- $this->handler->addToGroup('user1', $groupId, true);
+ $this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
$member = $this->handler->getGroupMemberInfo($groupId, 'user1');
$this->assertEquals('user1', $member['user_id']);
$this->assertEquals($groupId, $member['group_id']);
- $this->assertTrue($member['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_ADMIN, $member['role']);
}
public function testSetGroupMemberInfo() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
- $this->handler->addToGroup('user1', $groupId, true);
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
- $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', false));
+ $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', CustomGroupsDatabaseHandler::ROLE_MEMBER));
$member = $this->handler->getGroupMemberInfo($groupId, 'user1');
- $this->assertFalse($member['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $member['role']);
- $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', true));
+ $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', CustomGroupsDatabaseHandler::ROLE_ADMIN));
$member = $this->handler->getGroupMemberInfo($groupId, 'user1');
- $this->assertTrue($member['is_admin']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_ADMIN, $member['role']);
// setting to same value also returns true
- $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', true));
+ $this->assertTrue($this->handler->setGroupMemberInfo($groupId, 'user1', CustomGroupsDatabaseHandler::ROLE_ADMIN));
}
public function testInGroup() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
- $this->handler->addToGroup('user2', $groupId, false);
+ $this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
$this->assertTrue($this->handler->inGroup('user2', $groupId));
$this->assertFalse($this->handler->inGroup('user3', $groupId));
}
- public function testGetUserGroups() {
+ public function testGetUserMemberships() {
$groupId = $this->handler->createGroup('my_group', 'My Group');
$groupId2 = $this->handler->createGroup('my_group2', 'My Group Two');
- $this->handler->addToGroup('user2', $groupId, false);
- $this->handler->addToGroup('user1', $groupId, true);
- $this->handler->addToGroup('user2', $groupId2, false);
+ $this->handler->addToGroup('user2', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_ADMIN);
+ $this->handler->addToGroup('user2', $groupId2, CustomGroupsDatabaseHandler::ROLE_MEMBER);
- $groups = $this->handler->getUserGroups('user2');
+ $groups = $this->handler->getUserMemberships('user2');
$this->assertCount(2, $groups);
- $this->assertEquals($groupId, $groups[0]);
- $this->assertEquals($groupId2, $groups[1]);
+ $this->assertEquals($groupId, $groups[0]['group_id']);
+ $this->assertEquals('user2', $groups[0]['user_id']);
+ $this->assertEquals('my_group', $groups[0]['uri']);
+ $this->assertEquals('My Group', $groups[0]['display_name']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $groups[0]['role']);
+ $this->assertEquals($groupId2, $groups[1]['group_id']);
+ $this->assertEquals('user2', $groups[1]['user_id']);
+ $this->assertEquals('my_group2', $groups[1]['uri']);
+ $this->assertEquals('My Group Two', $groups[1]['display_name']);
+ $this->assertEquals(CustomGroupsDatabaseHandler::ROLE_MEMBER, $groups[1]['role']);
}
+ public function testGetUserMembershipsFiltered() {
+ $groupId = $this->handler->createGroup('my_group', 'My Group');
+ $groupId2 = $this->handler->createGroup('my_group2', 'My Group Two');
+
+ $this->handler->addToGroup('user1', $groupId, CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->handler->addToGroup('user1', $groupId2, CustomGroupsDatabaseHandler::ROLE_ADMIN);
+
+ $adminGroups = $this->handler->getUserMemberships('user1', CustomGroupsDatabaseHandler::ROLE_ADMIN);
+ $this->assertCount(1, $adminGroups);
+ $nonAdminGroups = $this->handler->getUserMemberships('user1', CustomGroupsDatabaseHandler::ROLE_MEMBER);
+ $this->assertCount(1, $nonAdminGroups);
+
+ $this->assertEquals($groupId, $nonAdminGroups[0]['group_id']);
+ $this->assertEquals($groupId2, $adminGroups[0]['group_id']);
+ }
}
diff --git a/tests/unit/Dav/GroupMembershipCollectionTest.php b/tests/unit/Dav/GroupMembershipCollectionTest.php
new file mode 100644
index 00000000..ad233ef3
--- /dev/null
+++ b/tests/unit/Dav/GroupMembershipCollectionTest.php
@@ -0,0 +1,413 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\GroupMembershipCollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\IUser;
+use Sabre\DAV\PropPatch;
+use OCA\CustomGroups\Dav\MembershipNode;
+use OCA\CustomGroups\Dav\MembershipHelper;
+use OCP\IGroupManager;
+
+/**
+ * Class GroupMembershipCollectionTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class GroupMembershipCollectionTest extends \Test\TestCase {
+ const CURRENT_USER = 'currentuser';
+ const NODE_USER = 'nodeuser';
+
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var GroupMembershipCollection
+ */
+ private $node;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->handler->expects($this->never())->method('getGroup');
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+
+ // currently logged in user
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::CURRENT_USER);
+ $this->userSession->method('getUser')->willReturn($user);
+
+ $nodeUser = $this->createMock(IUser::class);
+ $nodeUser->method('getUID')->willReturn(self::NODE_USER);
+ $this->userManager->method('get')->will(
+ $this->returnValueMap([
+ [self::NODE_USER, $nodeUser],
+ [strtoupper(self::NODE_USER), $nodeUser],
+ ]));
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+ $this->node = new GroupMembershipCollection(
+ ['group_id' => 1, 'uri' => 'group1', 'display_name' => 'Group One'],
+ $this->handler,
+ $this->helper
+ );
+ }
+
+ /**
+ * Sets a user's member info, for testing
+ *
+ * @param array $memberInfo user member info
+ */
+ private function setCurrentUserMemberInfo($memberInfo) {
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->with(1, self::CURRENT_USER)
+ ->willReturn($memberInfo);
+ }
+
+ private function setCurrentUserSuperAdmin($isSuperAdmin) {
+ $this->groupManager->expects($this->any())
+ ->method('isAdmin')
+ ->with(self::CURRENT_USER)
+ ->willReturn($isSuperAdmin);
+ }
+
+ public function testBase() {
+ $this->assertEquals('group1', $this->node->getName());
+ $this->assertNull($this->node->getLastModified());
+ }
+
+ public function testDeleteAsAdmin() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]);
+
+ $this->handler->expects($this->at(1))
+ ->method('deleteGroup')
+ ->with(1);
+
+ $this->node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteAsNonAdmin() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]);
+
+ $this->handler->expects($this->never())
+ ->method('deleteGroup');
+
+ $this->node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+
+ $this->handler->expects($this->never())
+ ->method('deleteGroup');
+
+ $this->node->delete();
+ }
+
+ public function testGetProperties() {
+ $props = $this->node->getProperties(null);
+ $this->assertEquals('Group One', $props[GroupMembershipCollection::PROPERTY_DISPLAY_NAME]);
+ $props = $this->node->getProperties([GroupMembershipCollection::PROPERTY_DISPLAY_NAME]);
+ $this->assertEquals('Group One', $props[GroupMembershipCollection::PROPERTY_DISPLAY_NAME]);
+ }
+
+ public function adminSetFlagProvider() {
+ return [
+ // admin can change display name
+ [false, true, 200, true],
+ // non-admin cannot change anything
+ [false, false, 403, false],
+ // non-member cannot change anything
+ [false, null, 403, false],
+ // super-admin non-member can change anything
+ [false, true, 200, true],
+ ];
+ }
+
+ /**
+ * @dataProvider adminSetFlagProvider
+ */
+ public function testSetProperties($isSuperAdmin, $currentUserRole, $statusCode, $called) {
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ if ($currentUserRole !== null) {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => $currentUserRole]);
+ } else {
+ $this->setCurrentUserMemberInfo(null);
+ }
+
+ if ($called) {
+ $this->handler->expects($this->at(1))
+ ->method('updateGroup')
+ ->with(1, 'group1', 'Group Renamed')
+ ->willReturn(true);
+ } else {
+ $this->handler->expects($this->never())
+ ->method('updateGroup');
+ }
+
+ $propPatch = new PropPatch([GroupMembershipCollection::PROPERTY_DISPLAY_NAME => 'Group Renamed']);
+ $this->node->propPatch($propPatch);
+
+ $propPatch->commit();
+ $this->assertEmpty($propPatch->getRemainingMutations());
+ $result = $propPatch->getResult();
+ $this->assertEquals($statusCode, $result[GroupMembershipCollection::PROPERTY_DISPLAY_NAME]);
+ }
+
+ public function rolesProvider() {
+ return [
+ [false, ['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]],
+ [false, ['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]],
+ [true, null],
+ ];
+ }
+
+ public function adminProvider() {
+ return [
+ [false, ['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]],
+ [true, null],
+ ];
+ }
+
+ /**
+ * @dataProvider adminProvider
+ */
+ public function testAddMemberAsAdmin($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserMemberInfo($currentMemberInfo);
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $this->handler->expects($this->once())
+ ->method('addToGroup')
+ ->with(self::NODE_USER, 1, false)
+ ->willReturn(true);
+
+ $this->node->createFile(self::NODE_USER);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\PreconditionFailed
+ * @dataProvider adminProvider
+ */
+ public function testAddMemberAsAdminFails($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserMemberInfo($currentMemberInfo);
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $this->handler->expects($this->once())
+ ->method('addToGroup')
+ ->with(self::NODE_USER, 1, false)
+ ->willReturn(false);
+
+ $this->node->createFile(self::NODE_USER);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\PreconditionFailed
+ * @dataProvider adminProvider
+ */
+ public function testAddNonExistingMemberAsAdmin($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserMemberInfo($currentMemberInfo);
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $this->handler->expects($this->never())
+ ->method('addToGroup');
+
+ $this->node->createFile('userunexist');
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\PreconditionFailed
+ * @dataProvider adminProvider
+ */
+ public function testAddNonExistingMemberMismatchCaseAsAdmin($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserMemberInfo($currentMemberInfo);
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $this->handler->expects($this->never())
+ ->method('addToGroup');
+
+ $this->node->createFile('USER2');
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testAddMemberAsNonAdmin() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]);
+
+ $this->handler->expects($this->never())
+ ->method('addToGroup');
+
+ $this->node->createFile(self::NODE_USER);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testAddMemberAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+
+ $this->handler->expects($this->never())
+ ->method('addToGroup');
+
+ $this->node->createFile(self::NODE_USER);
+ }
+
+ public function testIsMember() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]);
+ $this->handler->expects($this->any())
+ ->method('inGroup')
+ ->will($this->returnValueMap([
+ [self::NODE_USER, 1, true],
+ ['user3', 1, false],
+ ]));
+
+ $this->assertTrue($this->node->childExists(self::NODE_USER));
+ $this->assertFalse($this->node->childExists('user3'));
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testIsMemberAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+
+ $this->node->childExists(self::NODE_USER);
+ }
+
+ /**
+ * @dataProvider rolesProvider
+ */
+ public function testGetMember($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $membershipsMap = [
+ [1, self::NODE_USER, ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]],
+ ];
+ if (!is_null($currentMemberInfo)) {
+ $membershipsMap[] = [1, self::CURRENT_USER, $currentMemberInfo];
+ }
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->will($this->returnValueMap($membershipsMap));
+
+ $memberInfo = $this->node->getChild(self::NODE_USER);
+
+ $this->assertInstanceOf(MembershipNode::class, $memberInfo);
+ $this->assertEquals(self::NODE_USER, $memberInfo->getName());
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testGetMemberAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+
+ $this->node->getChild(self::NODE_USER);
+ }
+
+ /**
+ * @dataProvider rolesProvider
+ */
+ public function testGetMembers($isSuperAdmin, $currentMemberInfo) {
+ $this->setCurrentUserMemberInfo($currentMemberInfo);
+ $this->setCurrentUserSuperAdmin($isSuperAdmin);
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ ['group_id' => 1, 'user_id' => 'user3', 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER],
+ ]);
+
+ $memberInfos = $this->node->getChildren();
+
+ $this->assertCount(2, $memberInfos);
+ $this->assertInstanceOf(MembershipNode::class, $memberInfos[0]);
+ $this->assertEquals(self::NODE_USER, $memberInfos[0]->getName());
+ $this->assertInstanceOf(MembershipNode::class, $memberInfos[1]);
+ $this->assertEquals('user3', $memberInfos[1]->getName());
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testGetMembersAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+
+ $this->node->getChildren();
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testSetName() {
+ $this->node->setName('x');
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateDirectory() {
+ $this->node->createDirectory('somedir');
+ }
+}
diff --git a/tests/unit/Dav/GroupsCollectionTest.php b/tests/unit/Dav/GroupsCollectionTest.php
new file mode 100644
index 00000000..47203485
--- /dev/null
+++ b/tests/unit/Dav/GroupsCollectionTest.php
@@ -0,0 +1,193 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\GroupsCollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\IUser;
+use OCA\CustomGroups\Dav\GroupMembershipCollection;
+use OCA\CustomGroups\Dav\MembershipHelper;
+use OCP\IGroupManager;
+
+/**
+ * Class GroupsCollectionTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class GroupsCollectionTest extends \Test\TestCase {
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var GroupsCollection
+ */
+ private $collection;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->handler->expects($this->never())->method('getGroup');
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+ $this->collection = new GroupsCollection($this->handler, $this->helper);
+ }
+
+ public function testBase() {
+ $this->assertEquals('groups', $this->collection->getName());
+ $this->assertNull($this->collection->getLastModified());
+ }
+
+ public function testListGroups() {
+ $this->handler->expects($this->at(0))
+ ->method('getGroups')
+ ->will($this->returnValue([
+ ['group_id' => 1, 'uri' => 'group1', 'display_name' => 'Group One'],
+ ['group_id' => 2, 'uri' => 'group2', 'display_name' => 'Group Two'],
+ ]));
+
+ $nodes = $this->collection->getChildren();
+ $this->assertCount(2, $nodes);
+
+ $this->assertInstanceOf(GroupMembershipCollection::class, $nodes[0]);
+ $this->assertEquals('group1', $nodes[0]->getName());
+ $this->assertInstanceOf(GroupMembershipCollection::class, $nodes[1]);
+ $this->assertEquals('group2', $nodes[1]->getName());
+ }
+
+ public function testCreateGroup() {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user1');
+ $this->userSession->method('getUser')->willReturn($user);
+
+ $this->handler->expects($this->at(0))
+ ->method('createGroup')
+ ->with('group1', 'group1')
+ ->will($this->returnValue(1));
+ $this->handler->expects($this->at(1))
+ ->method('addToGroup')
+ ->with('user1', 1, true);
+
+ $this->collection->createDirectory('group1');
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateGroupAlreadyExists() {
+ $this->handler->expects($this->once())
+ ->method('createGroup')
+ ->with('group1', 'group1')
+ ->will($this->returnValue(null));
+ $this->handler->expects($this->never())
+ ->method('addToGroup')
+ ->with('user1', 1, true);
+
+ $this->collection->createDirectory('group1');
+ }
+
+ public function testGetGroup() {
+ $this->handler->expects($this->any())
+ ->method('getGroupByUri')
+ ->with('group1')
+ ->will($this->returnValue(['group_id' => 1, 'uri' => 'group1', 'display_name' => 'Group One']));
+
+ $groupNode = $this->collection->getChild('group1');
+ $this->assertInstanceOf(GroupMembershipCollection::class, $groupNode);
+ $this->assertEquals('group1', $groupNode->getName());
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\NotFound
+ */
+ public function testGetGroupNonExisting() {
+ $this->handler->expects($this->any())
+ ->method('getGroupByUri')
+ ->with('groupx')
+ ->will($this->returnValue(null));
+
+ $this->collection->getChild('groupx');
+ }
+
+ public function testGroupExists() {
+ $this->handler->expects($this->any())
+ ->method('getGroupByUri')
+ ->will($this->returnValueMap([
+ ['group1', ['group_id' => 1, 'uri' => 'group1', 'display_name' => 'Group One']],
+ ['group2', null],
+ ]));
+
+ $this->assertTrue($this->collection->childExists('group1'));
+ $this->assertFalse($this->collection->childExists('group2'));
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testSetName() {
+ $this->collection->setName('x');
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testDelete() {
+ $this->collection->delete();
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateFile() {
+ $this->collection->createFile('somefile.txt');
+ }
+}
diff --git a/tests/unit/Dav/MembershipHelperTest.php b/tests/unit/Dav/MembershipHelperTest.php
new file mode 100644
index 00000000..e4cd29bd
--- /dev/null
+++ b/tests/unit/Dav/MembershipHelperTest.php
@@ -0,0 +1,224 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCP\IUserSession;
+use OCP\IUserManager;
+use OCP\IGroupManager;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUser;
+use OCA\CustomGroups\Dav\MembershipHelper;
+
+/**
+ * Class MembershipHelperTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class MembershipHelperTest extends \Test\TestCase {
+
+ const CURRENT_USER = 'currentuser';
+
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ // currently logged in user
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::CURRENT_USER);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+ }
+
+ public function testGetUserId() {
+ $this->assertEquals(self::CURRENT_USER, $this->helper->getUserId());
+ }
+
+ public function testGetUser() {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('anotheruser');
+
+ $this->userManager->expects($this->once())
+ ->method('get')
+ ->with('anotheruser')
+ ->willReturn($user);
+
+ $this->assertEquals($user, $this->helper->getUser('anotheruser'));
+ }
+
+ public function isUserAdminDataProvider() {
+ return [
+ // regular member
+ [
+ false,
+ ['role' => CustomGroupsDatabaseHandler::ROLE_MEMBER],
+ false,
+ ],
+ // admin member
+ [
+ false,
+ ['role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ true,
+ ],
+ // super-admin but non-admin member
+ [
+ true,
+ ['role' => CustomGroupsDatabaseHandler::ROLE_MEMBER],
+ true,
+ ],
+ // non-member
+ [
+ false,
+ null,
+ false,
+ ],
+ // super-admin non-member
+ [
+ true,
+ null,
+ true,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider isUserAdminDataProvider
+ */
+ public function testIsUserAdmin($isSuperAdmin, $memberInfo, $expectedResult) {
+ $this->groupManager->expects($this->once())
+ ->method('isAdmin')
+ ->with(self::CURRENT_USER)
+ ->willReturn($isSuperAdmin);
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->with('group1', self::CURRENT_USER)
+ ->willReturn($memberInfo);
+
+ $this->assertEquals($expectedResult, $this->helper->isUserAdmin('group1'));
+ }
+
+ public function isUserMemberDataProvider() {
+ return [
+ // regular member
+ [
+ ['role' => CustomGroupsDatabaseHandler::ROLE_MEMBER],
+ true,
+ ],
+ // admin member
+ [
+ ['role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ true,
+ ],
+ // non-member
+ [
+ null,
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider isUserMemberDataProvider
+ */
+ public function testIsUserMember($memberInfo, $expectedResult) {
+ $this->handler->expects($this->once())
+ ->method('getGroupMemberInfo')
+ ->with('group1', self::CURRENT_USER)
+ ->willReturn($memberInfo);
+
+ $this->assertEquals($expectedResult, $this->helper->isUserMember('group1'));
+ }
+
+ public function isTheOnlyAdminDataProvider() {
+ return [
+ // user is not the last admin
+ [
+ [
+ ['user_id' => 'admin1'],
+ ['user_id' => 'admin2'],
+ ],
+ false,
+ ],
+ // user is the last admin
+ [
+ [
+ ['user_id' => 'admin1'],
+ ],
+ true,
+ ],
+ // someone else is the last admin
+ [
+ [
+ ['user_id' => 'admin2'],
+ ],
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider isTheOnlyAdminDataProvider
+ */
+ public function testIsTheOnlyAdmin($memberInfo, $expectedResult) {
+ $this->handler->expects($this->once())
+ ->method('getGroupMembers')
+ ->with('group1', CustomGroupsDatabaseHandler::ROLE_ADMIN)
+ ->willReturn($memberInfo);
+
+ $this->assertEquals($expectedResult, $this->helper->isTheOnlyAdmin('group1', 'admin1'));
+ }
+}
diff --git a/tests/unit/Dav/MembershipNodeTest.php b/tests/unit/Dav/MembershipNodeTest.php
new file mode 100644
index 00000000..fe1b93b4
--- /dev/null
+++ b/tests/unit/Dav/MembershipNodeTest.php
@@ -0,0 +1,432 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\MembershipNode;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\IUser;
+use Sabre\DAV\PropPatch;
+use OCA\CustomGroups\Dav\MembershipHelper;
+use OCP\IGroupManager;
+
+/**
+ * Class MembershipNodeTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class MembershipNodeTest extends \Test\TestCase {
+
+ const CURRENT_USER = 'currentuser';
+ const NODE_USER = 'nodeuser';
+
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var MembershipNode
+ */
+ private $node;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->handler->expects($this->never())->method('getGroup');
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ // currently logged in user
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::CURRENT_USER);
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+ $this->node = new MembershipNode(
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ self::NODE_USER,
+ $this->handler,
+ $this->helper
+ );
+ }
+
+ /**
+ * Sets a user's member info, for testing
+ *
+ * @param array $memberInfo user member info
+ */
+ private function setCurrentUserMemberInfo($memberInfo) {
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->with(1, self::CURRENT_USER)
+ ->willReturn($memberInfo);
+ }
+
+ public function testBase() {
+ $this->assertEquals(self::NODE_USER, $this->node->getName());
+ $this->assertNull($this->node->getLastModified());
+ }
+
+ public function testNodeName() {
+ $node = new MembershipNode(
+ ['group_id' => 1, 'uri' => 'group1', 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ 'group1',
+ $this->handler,
+ $this->helper
+ );
+ $this->assertEquals('group1', $node->getName());
+ }
+
+ public function testDeleteAsAdmin() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]);
+ $this->handler->expects($this->once())
+ ->method('removeFromGroup')
+ ->with(self::NODE_USER, 1)
+ ->willReturn(true);
+
+ $this->node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\PreconditionFailed
+ */
+ public function testDeleteAsAdminFailed() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]);
+ $this->handler->expects($this->once())
+ ->method('removeFromGroup')
+ ->with(self::NODE_USER, 1)
+ ->willReturn(false);
+
+ $this->node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteAsNonAdmin() {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER]);
+ $this->handler->expects($this->never())
+ ->method('removeFromGroup');
+
+ $this->node->delete();
+ }
+
+ /**
+ * Creates a node for the NODE_USER user and give that
+ * user permissions if needed
+ *
+ * @param int $role admin perms for the NODE_USER
+ * @return MembershipNode new node
+ */
+ private function makeSelfNode($role) {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::NODE_USER);
+ $userSession = $this->createMock(IUserSession::class);
+ $userSession->method('getUser')->willReturn($user);
+
+ $helper = new MembershipHelper(
+ $this->handler,
+ $userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+
+ $memberInfo = ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => $role];
+ $node = new MembershipNode(
+ $memberInfo,
+ self::NODE_USER,
+ $this->handler,
+ $helper
+ );
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->with(1, self::NODE_USER)
+ ->willReturn($memberInfo);
+ return $node;
+ }
+
+ public function testDeleteSelfAsNonAdmin() {
+ $node = $this->makeSelfNode(CustomGroupsDatabaseHandler::ROLE_MEMBER);
+
+ $this->handler->expects($this->once())
+ ->method('removeFromGroup')
+ ->with(self::NODE_USER, 1)
+ ->willReturn(true);
+
+ $node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteAsNonMember() {
+ $this->setCurrentUserMemberInfo(null);
+ $this->handler->expects($this->never())
+ ->method('removeFromGroup');
+
+ $this->node->delete();
+ }
+
+ /**
+ * Super admin can delete any member
+ */
+ public function testDeleteAsSuperAdmin() {
+ $this->setCurrentUserMemberInfo(null);
+ $this->groupManager->method('isAdmin')
+ ->with(self::CURRENT_USER)
+ ->willReturn(true);
+
+ $this->handler->expects($this->once())
+ ->method('removeFromGroup')
+ ->with(self::NODE_USER, 1)
+ ->willReturn(true);
+
+ $this->node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteSelfAsLastAdmin() {
+ $node = $this->makeSelfNode(CustomGroupsDatabaseHandler::ROLE_ADMIN);
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1, CustomGroupsDatabaseHandler::ROLE_ADMIN)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]
+ ]);
+
+ $this->handler->expects($this->never())
+ ->method('removeFromGroup');
+
+ $node->delete();
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\Forbidden
+ */
+ public function testDeleteLastAdminAsSuperAdmin() {
+ $node = $this->makeSelfNode(CustomGroupsDatabaseHandler::ROLE_MEMBER);
+
+ $this->groupManager->method('isAdmin')
+ ->with(self::NODE_USER)
+ ->willReturn(true);
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1, CustomGroupsDatabaseHandler::ROLE_ADMIN)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]
+ ]);
+
+ $this->handler->expects($this->never())
+ ->method('removeFromGroup');
+
+ $node->delete();
+ }
+
+ public function propsProvider() {
+ return [
+ [
+ MembershipNode::PROPERTY_ROLE,
+ CustomGroupsDatabaseHandler::ROLE_ADMIN,
+ CustomGroupsDatabaseHandler::ROLE_ADMIN,
+ ],
+ [
+ MembershipNode::PROPERTY_ROLE,
+ CustomGroupsDatabaseHandler::ROLE_MEMBER,
+ CustomGroupsDatabaseHandler::ROLE_MEMBER,
+ ],
+ [
+ MembershipNode::PROPERTY_USER_ID,
+ self::NODE_USER,
+ ],
+ [
+ MembershipNode::PROPERTY_GROUP_URI,
+ 'group1',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider propsProvider
+ */
+ public function testGetProperties($propName, $propValue, $roleValue = 0) {
+ $node = new MembershipNode(
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => $roleValue, 'uri' => 'group1'],
+ self::NODE_USER,
+ $this->handler,
+ $this->helper
+ );
+
+ $props = $node->getProperties(null);
+ $this->assertSame($propValue, $props[$propName]);
+ $props = $node->getProperties([$propName]);
+ $this->assertSame($propValue, $props[$propName]);
+ }
+
+ public function adminSetFlagProvider() {
+ return [
+ // admin can change flag for others
+ [false, CustomGroupsDatabaseHandler::ROLE_ADMIN, CustomGroupsDatabaseHandler::ROLE_ADMIN, 200, true],
+ [false, CustomGroupsDatabaseHandler::ROLE_ADMIN, CustomGroupsDatabaseHandler::ROLE_MEMBER, 200, true],
+ // non-admin cannot change anything
+ [false, CustomGroupsDatabaseHandler::ROLE_MEMBER, CustomGroupsDatabaseHandler::ROLE_ADMIN, 403, false],
+ [false, CustomGroupsDatabaseHandler::ROLE_MEMBER, CustomGroupsDatabaseHandler::ROLE_MEMBER, 403, false],
+ // non-member cannot change anything
+ [false, null, CustomGroupsDatabaseHandler::ROLE_ADMIN, 403, false],
+ [false, null, CustomGroupsDatabaseHandler::ROLE_MEMBER, 403, false],
+ // super-admin can change even as non-member
+ [true, null, CustomGroupsDatabaseHandler::ROLE_ADMIN, 200, true],
+ [true, null, CustomGroupsDatabaseHandler::ROLE_MEMBER, 200, true],
+ ];
+ }
+
+ /**
+ * @dataProvider adminSetFlagProvider
+ */
+ public function testSetProperties($isSuperAdmin, $currentUserRole, $roleToSet, $statusCode, $called) {
+ if ($currentUserRole !== null) {
+ $this->setCurrentUserMemberInfo(['group_id' => 1, 'user_id' => self::CURRENT_USER, 'role' => $currentUserRole]);
+ } else {
+ $this->setCurrentUserMemberInfo(null);
+ }
+
+ $this->groupManager->method('isAdmin')
+ ->with(self::CURRENT_USER)
+ ->willReturn($isSuperAdmin);
+
+ if ($called) {
+ $this->handler->expects($this->once())
+ ->method('setGroupMemberInfo')
+ ->with(1, self::NODE_USER, $roleToSet)
+ ->willReturn(true);
+ } else {
+ $this->handler->expects($this->never())
+ ->method('setGroupMemberInfo');
+ }
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1, true)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => 'someotheradmin', 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN],
+ ]);
+
+ $propPatch = new PropPatch([MembershipNode::PROPERTY_ROLE => $roleToSet]);
+ $this->node->propPatch($propPatch);
+
+ $propPatch->commit();
+ $this->assertEmpty($propPatch->getRemainingMutations());
+ $result = $propPatch->getResult();
+ $this->assertEquals($statusCode, $result[MembershipNode::PROPERTY_ROLE]);
+ }
+
+ /**
+ * Cannot remove admin perms from last admin
+ */
+ public function testUnsetSelfAdminWhenLastAdmin() {
+ $this->groupManager->method('isAdmin')
+ ->with(self::CURRENT_USER)
+ ->willReturn(true);
+
+ $this->handler->expects($this->never())
+ ->method('setGroupMemberInfo');
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1, true)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]
+ ]);
+
+ $propPatch = new PropPatch([MembershipNode::PROPERTY_ROLE => 0]);
+ $this->node->propPatch($propPatch);
+
+ $propPatch->commit();
+ $this->assertEmpty($propPatch->getRemainingMutations());
+ $result = $propPatch->getResult();
+ $this->assertEquals(403, $result[MembershipNode::PROPERTY_ROLE]);
+ }
+
+ /**
+ * Cannot remove admin perms from last admin
+ */
+ public function testUnsetdminWhenLastAdminAsSuperAdmin() {
+ $node = $this->makeSelfNode(CustomGroupsDatabaseHandler::ROLE_ADMIN);
+
+ $this->handler->expects($this->never())
+ ->method('setGroupMemberInfo');
+
+ $this->handler->expects($this->any())
+ ->method('getGroupMembers')
+ ->with(1, true)
+ ->willReturn([
+ ['group_id' => 1, 'user_id' => self::NODE_USER, 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN]
+ ]);
+
+ $propPatch = new PropPatch([MembershipNode::PROPERTY_ROLE => 0]);
+ $node->propPatch($propPatch);
+
+ $propPatch->commit();
+ $this->assertEmpty($propPatch->getRemainingMutations());
+ $result = $propPatch->getResult();
+ $this->assertEquals(403, $result[MembershipNode::PROPERTY_ROLE]);
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testSetName() {
+ $this->node->setName('x');
+ }
+}
diff --git a/tests/unit/Dav/RootCollectionTest.php b/tests/unit/Dav/RootCollectionTest.php
new file mode 100644
index 00000000..5fa58917
--- /dev/null
+++ b/tests/unit/Dav/RootCollectionTest.php
@@ -0,0 +1,63 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\RootCollection;
+use OCA\CustomGroups\Dav\UsersCollection;
+use OCA\CustomGroups\Dav\GroupsCollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCA\CustomGroups\Dav\MembershipHelper;
+
+/**
+ * Class RootCollectionTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class RootCollectionTest extends \Test\TestCase {
+
+ public function setUp() {
+ parent::setUp();
+ $handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $helper = $this->createMock(MembershipHelper::class);
+
+ $this->collection = new RootCollection(
+ $handler,
+ $helper
+ );
+ }
+
+ public function testGetGroups() {
+ $groups = $this->collection->getChild('groups');
+ $this->assertInstanceOf(GroupsCollection::class, $groups);
+ }
+
+ public function testGetUsers() {
+ $users = $this->collection->getChild('users');
+ $this->assertInstanceOf(UsersCollection::class, $users);
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\NotFound
+ */
+ public function testGetNonExisting() {
+ $this->collection->getChild('somethingelse');
+ }
+}
diff --git a/tests/unit/Dav/UserMembershipCollectionTest.php b/tests/unit/Dav/UserMembershipCollectionTest.php
new file mode 100644
index 00000000..9fc24135
--- /dev/null
+++ b/tests/unit/Dav/UserMembershipCollectionTest.php
@@ -0,0 +1,213 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\UserMembershipCollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\IUser;
+use Sabre\DAV\PropPatch;
+use OCA\CustomGroups\Dav\MembershipNode;
+use OCA\CustomGroups\Dav\MembershipHelper;
+use OCP\IGroupManager;
+
+/**
+ * Class UserMembershipCollectionTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class UserMembershipCollectionTest extends \Test\TestCase {
+ const CURRENT_USER = 'currentuser';
+
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var UserMembershipCollection
+ */
+ private $node;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->handler->expects($this->never())->method('getGroup');
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+
+ // currently logged in user
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::CURRENT_USER);
+ $this->userSession->method('getUser')->willReturn($user);
+
+ $this->handler->expects($this->any())
+ ->method('getGroupByUri')
+ ->will($this->returnValueMap([
+ ['group1', ['group_id' => 1]],
+ ['group2', ['group_id' => 2]],
+ ['group3', null],
+ ]));
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+ $this->node = new UserMembershipCollection(
+ self::CURRENT_USER,
+ $this->handler,
+ $this->helper
+ );
+ }
+
+ public function testBase() {
+ $this->assertEquals(self::CURRENT_USER, $this->node->getName());
+ $this->assertNull($this->node->getLastModified());
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testDelete() {
+ $this->node->delete();
+ }
+
+ public function testIsMember() {
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->will($this->returnValueMap([
+ [1, self::CURRENT_USER, [
+ 'group_id' => 1,
+ 'uri' => 'group1',
+ 'user_id' => self::CURRENT_USER,
+ 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER
+ ]],
+ ]));
+
+ $this->assertTrue($this->node->childExists('group1'));
+ $this->assertFalse($this->node->childExists('group2'));
+ $this->assertFalse($this->node->childExists('group3'));
+ }
+
+ public function testIsMemberAsNonMember() {
+ $this->assertFalse($this->node->childExists('group1'));
+ }
+
+ public function testGetMember() {
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->will($this->returnValueMap([
+ [1, self::CURRENT_USER, [
+ 'group_id' => 1,
+ 'uri' => 'group1',
+ 'user_id' => self::CURRENT_USER,
+ 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER
+ ]],
+ ]));
+
+ $memberInfo = $this->node->getChild('group1');
+
+ $this->assertInstanceOf(MembershipNode::class, $memberInfo);
+ $this->assertEquals('group1', $memberInfo->getName());
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception\NotFound
+ */
+ public function testGetMemberAsNonMember() {
+ $this->handler->expects($this->any())
+ ->method('getGroupMemberInfo')
+ ->will($this->returnValue([]));
+
+ $this->node->getChild(self::CURRENT_USER);
+ }
+
+ public function testGetMembers() {
+ $this->handler->expects($this->any())
+ ->method('getUserMemberships')
+ ->with(self::CURRENT_USER)
+ ->willReturn([[
+ 'group_id' => 1,
+ 'uri' => 'group1',
+ 'user_id' => self::CURRENT_USER,
+ 'role' => CustomGroupsDatabaseHandler::ROLE_ADMIN
+ ], [
+ 'group_id' => 2,
+ 'uri' => 'group2',
+ 'user_id' => self::CURRENT_USER,
+ 'role' => CustomGroupsDatabaseHandler::ROLE_MEMBER
+ ],
+ ]);
+
+ $memberInfos = $this->node->getChildren();
+
+ $this->assertCount(2, $memberInfos);
+ $this->assertInstanceOf(MembershipNode::class, $memberInfos[0]);
+ $this->assertEquals('group1', $memberInfos[0]->getName());
+ $this->assertInstanceOf(MembershipNode::class, $memberInfos[1]);
+ $this->assertEquals('group2', $memberInfos[1]->getName());
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testSetName() {
+ $this->node->setName('x');
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateFile() {
+ $this->node->createFile('somedir');
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateDirectory() {
+ $this->node->createDirectory('somedir');
+ }
+}
diff --git a/tests/unit/Dav/UsersCollectionTest.php b/tests/unit/Dav/UsersCollectionTest.php
new file mode 100644
index 00000000..a7370da3
--- /dev/null
+++ b/tests/unit/Dav/UsersCollectionTest.php
@@ -0,0 +1,160 @@
+
+ *
+ * @copyright Copyright (c) 2016, 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\CustomGroups\Tests\unit\Dav;
+
+use OCA\CustomGroups\Dav\UsersCollection;
+use OCA\CustomGroups\CustomGroupsDatabaseHandler;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\IUser;
+use OCA\CustomGroups\Dav\UserMembershipCollection;
+use OCA\CustomGroups\Dav\MembershipHelper;
+use OCP\IGroupManager;
+
+/**
+ * Class UsersCollectionTest
+ *
+ * @package OCA\CustomGroups\Tests\Unit
+ */
+class UsersCollectionTest extends \Test\TestCase {
+
+ const USER = 'user1';
+
+ /**
+ * @var CustomGroupsDatabaseHandler
+ */
+ private $handler;
+
+ /**
+ * @var UsersCollection
+ */
+ private $collection;
+
+ /**
+ * @var MembershipHelper
+ */
+ private $helper;
+
+ /**
+ * @var IUserManager
+ */
+ private $userManager;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ public function setUp() {
+ parent::setUp();
+ $this->handler = $this->createMock(CustomGroupsDatabaseHandler::class);
+ $this->handler->expects($this->never())->method('getGroup');
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn(self::USER);
+ $this->userSession->method('getUser')->willReturn($user);
+
+ $this->helper = new MembershipHelper(
+ $this->handler,
+ $this->userSession,
+ $this->userManager,
+ $this->groupManager
+ );
+
+ $this->collection = new UsersCollection($this->handler, $this->helper);
+ }
+
+ public function testBase() {
+ $this->assertEquals('users', $this->collection->getName());
+ $this->assertNull($this->collection->getLastModified());
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testListUsers() {
+ $this->collection->getChildren();
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateUser() {
+ $this->collection->createDirectory('user1');
+ }
+
+ public function testGetCurrentUser() {
+ $membershipCollection = $this->collection->getChild(self::USER);
+ $this->assertInstanceOf(UserMembershipCollection::class, $membershipCollection);
+ $this->assertEquals(self::USER, $membershipCollection->getName());
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\Forbidden
+ */
+ public function testGetAnotherUser() {
+ $this->collection->getChild('another');
+ }
+
+ public function testGetAnotherUserAsAdmin() {
+ $this->groupManager->method('isAdmin')->with(self::USER)->willReturn(true);
+ $membershipCollection = $this->collection->getChild('another');
+ $this->assertInstanceOf(UserMembershipCollection::class, $membershipCollection);
+ $this->assertEquals('another', $membershipCollection->getName());
+ }
+
+ public function testUserExistsCurrent() {
+ $this->assertTrue($this->collection->childExists(self::USER));
+ }
+
+ public function testUserExistsAnother() {
+ $this->assertFalse($this->collection->childExists('another'));
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testSetName() {
+ $this->collection->setName('x');
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testDelete() {
+ $this->collection->delete();
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\MethodNotAllowed
+ */
+ public function testCreateFile() {
+ $this->collection->createFile('somefile.txt');
+ }
+}
diff --git a/tests/unit/bootstrap.php b/tests/unit/bootstrap.php
index 0b1324d6..f452e4e3 100644
--- a/tests/unit/bootstrap.php
+++ b/tests/unit/bootstrap.php
@@ -3,7 +3,7 @@
* @author Joas Schilling
* @author Jörn Friedrich Dreyer
*
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify