GroupPrincipalBackend.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. * @copyright Copyright (c) 2018, Georg Ehrke
  5. *
  6. * @author Roeland Jago Douma <roeland@famdouma.nl>
  7. * @author Thomas Müller <thomas.mueller@tmit.eu>
  8. * @author Georg Ehrke <oc.list@georgehrke.com>
  9. *
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OCA\DAV\DAV;
  26. use OCP\IGroup;
  27. use OCP\IGroupManager;
  28. use OCP\IUserSession;
  29. use OCP\Share\IManager as IShareManager;
  30. use OCP\IUser;
  31. use Sabre\DAV\Exception;
  32. use \Sabre\DAV\PropPatch;
  33. use Sabre\DAVACL\PrincipalBackend\BackendInterface;
  34. class GroupPrincipalBackend implements BackendInterface {
  35. const PRINCIPAL_PREFIX = 'principals/groups';
  36. /** @var IGroupManager */
  37. private $groupManager;
  38. /** @var IUserSession */
  39. private $userSession;
  40. /** @var IShareManager */
  41. private $shareManager;
  42. /**
  43. * @param IGroupManager $IGroupManager
  44. * @param IUserSession $userSession
  45. * @param IShareManager $shareManager
  46. */
  47. public function __construct(IGroupManager $IGroupManager,
  48. IUserSession $userSession,
  49. IShareManager $shareManager) {
  50. $this->groupManager = $IGroupManager;
  51. $this->userSession = $userSession;
  52. $this->shareManager = $shareManager;
  53. }
  54. /**
  55. * Returns a list of principals based on a prefix.
  56. *
  57. * This prefix will often contain something like 'principals'. You are only
  58. * expected to return principals that are in this base path.
  59. *
  60. * You are expected to return at least a 'uri' for every user, you can
  61. * return any additional properties if you wish so. Common properties are:
  62. * {DAV:}displayname
  63. *
  64. * @param string $prefixPath
  65. * @return string[]
  66. */
  67. public function getPrincipalsByPrefix($prefixPath) {
  68. $principals = [];
  69. if ($prefixPath === self::PRINCIPAL_PREFIX) {
  70. foreach($this->groupManager->search('') as $user) {
  71. $principals[] = $this->groupToPrincipal($user);
  72. }
  73. }
  74. return $principals;
  75. }
  76. /**
  77. * Returns a specific principal, specified by it's path.
  78. * The returned structure should be the exact same as from
  79. * getPrincipalsByPrefix.
  80. *
  81. * @param string $path
  82. * @return array
  83. */
  84. public function getPrincipalByPath($path) {
  85. $elements = explode('/', $path, 3);
  86. if ($elements[0] !== 'principals') {
  87. return null;
  88. }
  89. if ($elements[1] !== 'groups') {
  90. return null;
  91. }
  92. $name = urldecode($elements[2]);
  93. $group = $this->groupManager->get($name);
  94. if (!is_null($group)) {
  95. return $this->groupToPrincipal($group);
  96. }
  97. return null;
  98. }
  99. /**
  100. * Returns the list of members for a group-principal
  101. *
  102. * @param string $principal
  103. * @return string[]
  104. * @throws Exception
  105. */
  106. public function getGroupMemberSet($principal) {
  107. $elements = explode('/', $principal);
  108. if ($elements[0] !== 'principals') {
  109. return [];
  110. }
  111. if ($elements[1] !== 'groups') {
  112. return [];
  113. }
  114. $name = $elements[2];
  115. $group = $this->groupManager->get($name);
  116. if (is_null($group)) {
  117. return [];
  118. }
  119. return array_map(function($user) {
  120. return $this->userToPrincipal($user);
  121. }, $group->getUsers());
  122. }
  123. /**
  124. * Returns the list of groups a principal is a member of
  125. *
  126. * @param string $principal
  127. * @return array
  128. * @throws Exception
  129. */
  130. public function getGroupMembership($principal) {
  131. return [];
  132. }
  133. /**
  134. * Updates the list of group members for a group principal.
  135. *
  136. * The principals should be passed as a list of uri's.
  137. *
  138. * @param string $principal
  139. * @param string[] $members
  140. * @throws Exception
  141. */
  142. public function setGroupMemberSet($principal, array $members) {
  143. throw new Exception('Setting members of the group is not supported yet');
  144. }
  145. /**
  146. * @param string $path
  147. * @param PropPatch $propPatch
  148. * @return int
  149. */
  150. function updatePrincipal($path, PropPatch $propPatch) {
  151. return 0;
  152. }
  153. /**
  154. * @param string $prefixPath
  155. * @param array $searchProperties
  156. * @param string $test
  157. * @return array
  158. */
  159. function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
  160. $results = [];
  161. if (\count($searchProperties) === 0) {
  162. return [];
  163. }
  164. if ($prefixPath !== self::PRINCIPAL_PREFIX) {
  165. return [];
  166. }
  167. // If sharing is disabled, return the empty array
  168. $shareAPIEnabled = $this->shareManager->shareApiEnabled();
  169. if (!$shareAPIEnabled) {
  170. return [];
  171. }
  172. // If sharing is restricted to group members only,
  173. // return only members that have groups in common
  174. $restrictGroups = false;
  175. if ($this->shareManager->shareWithGroupMembersOnly()) {
  176. $user = $this->userSession->getUser();
  177. if (!$user) {
  178. return [];
  179. }
  180. $restrictGroups = $this->groupManager->getUserGroupIds($user);
  181. }
  182. foreach ($searchProperties as $prop => $value) {
  183. switch ($prop) {
  184. case '{DAV:}displayname':
  185. $groups = $this->groupManager->search($value);
  186. $results[] = array_reduce($groups, function(array $carry, IGroup $group) use ($restrictGroups) {
  187. $gid = $group->getGID();
  188. // is sharing restricted to groups only?
  189. if ($restrictGroups !== false) {
  190. if (!\in_array($gid, $restrictGroups, true)) {
  191. return $carry;
  192. }
  193. }
  194. $carry[] = self::PRINCIPAL_PREFIX . '/' . $gid;
  195. return $carry;
  196. }, []);
  197. break;
  198. default:
  199. $results[] = [];
  200. break;
  201. }
  202. }
  203. // results is an array of arrays, so this is not the first search result
  204. // but the results of the first searchProperty
  205. if (count($results) === 1) {
  206. return $results[0];
  207. }
  208. switch ($test) {
  209. case 'anyof':
  210. return array_values(array_unique(array_merge(...$results)));
  211. case 'allof':
  212. default:
  213. return array_values(array_intersect(...$results));
  214. }
  215. }
  216. /**
  217. * @param string $uri
  218. * @param string $principalPrefix
  219. * @return string
  220. */
  221. function findByUri($uri, $principalPrefix) {
  222. // If sharing is disabled, return the empty array
  223. $shareAPIEnabled = $this->shareManager->shareApiEnabled();
  224. if (!$shareAPIEnabled) {
  225. return null;
  226. }
  227. // If sharing is restricted to group members only,
  228. // return only members that have groups in common
  229. $restrictGroups = false;
  230. if ($this->shareManager->shareWithGroupMembersOnly()) {
  231. $user = $this->userSession->getUser();
  232. if (!$user) {
  233. return null;
  234. }
  235. $restrictGroups = $this->groupManager->getUserGroupIds($user);
  236. }
  237. if (strpos($uri, 'principal:principals/groups/') === 0) {
  238. $name = urlencode(substr($uri, 28));
  239. if ($restrictGroups !== false && !\in_array($name, $restrictGroups, true)) {
  240. return null;
  241. }
  242. return substr($uri, 10);
  243. }
  244. return null;
  245. }
  246. /**
  247. * @param IGroup $group
  248. * @return array
  249. */
  250. protected function groupToPrincipal($group) {
  251. $groupId = $group->getGID();
  252. // getDisplayName returns UID if none
  253. $displayName = $group->getDisplayName();
  254. return [
  255. 'uri' => 'principals/groups/' . urlencode($groupId),
  256. '{DAV:}displayname' => $displayName,
  257. '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'GROUP',
  258. ];
  259. }
  260. /**
  261. * @param IUser $user
  262. * @return array
  263. */
  264. protected function userToPrincipal($user) {
  265. $userId = $user->getUID();
  266. // getDisplayName returns UID if none
  267. $displayName = $user->getDisplayName();
  268. $principal = [
  269. 'uri' => 'principals/users/' . $userId,
  270. '{DAV:}displayname' => $displayName,
  271. '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
  272. ];
  273. $email = $user->getEMailAddress();
  274. if (!empty($email)) {
  275. $principal['{http://sabredav.org/ns}email-address'] = $email;
  276. }
  277. return $principal;
  278. }
  279. }