1<?php
2/**
3 * @copyright Copyright (c) 2016, ownCloud, Inc.
4 *
5 * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
6 * @author Bart Visscher <bartv@thisnet.nl>
7 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
8 * @author Joas Schilling <coding@schilljs.com>
9 * @author Lukas Reschke <lukas@statuscode.ch>
10 * @author Morris Jobke <hey@morrisjobke.de>
11 * @author Robin Appelman <robin@icewind.nl>
12 * @author Robin McCorkell <robin@mccorkell.me.uk>
13 *
14 * @license AGPL-3.0
15 *
16 * This code is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU Affero General Public License, version 3,
18 * as published by the Free Software Foundation.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Affero General Public License for more details.
24 *
25 * You should have received a copy of the GNU Affero General Public License, version 3,
26 * along with this program. If not, see <http://www.gnu.org/licenses/>
27 *
28 */
29namespace OCA\User_LDAP\Jobs;
30
31use OC\BackgroundJob\TimedJob;
32use OCA\User_LDAP\Group_Proxy;
33use OCP\EventDispatcher\IEventDispatcher;
34use OCP\Group\Events\UserAddedEvent;
35use OCP\Group\Events\UserRemovedEvent;
36use OCP\IDBConnection;
37use OCP\IGroupManager;
38use OCP\IUser;
39use OCP\IUserManager;
40use Psr\Log\LoggerInterface;
41
42class UpdateGroups extends TimedJob {
43	private $groupsFromDB;
44
45	/** @var Group_Proxy */
46	private $groupBackend;
47	/** @var IEventDispatcher */
48	private $dispatcher;
49	/** @var IGroupManager */
50	private $groupManager;
51	/** @var IUserManager */
52	private $userManager;
53	/** @var LoggerInterface */
54	private $logger;
55	/** @var IDBConnection */
56	private $dbc;
57
58	public function __construct(
59		Group_Proxy $groupBackend,
60		IEventDispatcher $dispatcher,
61		IGroupManager $groupManager,
62		IUserManager $userManager,
63		LoggerInterface $logger,
64		IDBConnection $dbc
65	) {
66		$this->interval = $this->getRefreshInterval();
67		$this->groupBackend = $groupBackend;
68		$this->dispatcher = $dispatcher;
69		$this->groupManager = $groupManager;
70		$this->userManager = $userManager;
71		$this->logger = $logger;
72		$this->dbc = $dbc;
73	}
74
75	/**
76	 * @return int
77	 */
78	private function getRefreshInterval() {
79		//defaults to every hour
80		return \OC::$server->getConfig()->getAppValue('user_ldap', 'bgjRefreshInterval', 3600);
81	}
82
83	/**
84	 * @param mixed $argument
85	 */
86	public function run($argument) {
87		$this->updateGroups();
88	}
89
90	public function updateGroups() {
91		$this->logger->debug(
92			'Run background job "updateGroups"',
93			['app' => 'user_ldap']
94		);
95
96		$knownGroups = array_keys($this->getKnownGroups());
97		$actualGroups = $this->groupBackend->getGroups();
98
99		if (empty($actualGroups) && empty($knownGroups)) {
100			$this->logger->info(
101				'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.',
102				['app' => 'user_ldap']
103			);
104			return;
105		}
106
107		$this->handleKnownGroups(array_intersect($actualGroups, $knownGroups));
108		$this->handleCreatedGroups(array_diff($actualGroups, $knownGroups));
109		$this->handleRemovedGroups(array_diff($knownGroups, $actualGroups));
110
111		$this->logger->debug(
112			'bgJ "updateGroups" – Finished.',
113			['app' => 'user_ldap']
114		);
115	}
116
117	/**
118	 * @return array
119	 */
120	private function getKnownGroups() {
121		if (is_array($this->groupsFromDB)) {
122			$this->groupsFromDB;
123		}
124		$qb = $this->dbc->getQueryBuilder();
125		$qb->select(['owncloudname', 'owncloudusers'])
126			->from('ldap_group_members');
127
128		$qResult = $qb->execute();
129		$result = $qResult->fetchAll();
130		$qResult->closeCursor();
131
132		$this->groupsFromDB = [];
133		foreach ($result as $dataset) {
134			$this->groupsFromDB[$dataset['owncloudname']] = $dataset;
135		}
136
137		return $this->groupsFromDB;
138	}
139
140	private function handleKnownGroups(array $groups) {
141		$this->logger->debug(
142			'bgJ "updateGroups" – Dealing with known Groups.',
143			['app' => 'user_ldap']
144		);
145		$qb = $this->dbc->getQueryBuilder();
146		$qb->update('ldap_group_members')
147			->set('owncloudusers', $qb->createParameter('members'))
148			->where($qb->expr()->eq('owncloudname', $qb->createParameter('groupId')));
149
150		if (!is_array($this->groupsFromDB)) {
151			$this->getKnownGroups();
152		}
153		foreach ($groups as $group) {
154			$knownUsers = unserialize($this->groupsFromDB[$group]['owncloudusers']);
155			$actualUsers = $this->groupBackend->usersInGroup($group);
156			$hasChanged = false;
157
158			$groupObject = $this->groupManager->get($group);
159			foreach (array_diff($knownUsers, $actualUsers) as $removedUser) {
160				$userObject = $this->userManager->get($removedUser);
161				if ($userObject instanceof IUser) {
162					$this->dispatcher->dispatchTyped(new UserRemovedEvent($groupObject, $userObject));
163				}
164				$this->logger->info(
165					'bgJ "updateGroups" – {user} removed from {group}',
166					[
167						'app' => 'user_ldap',
168						'user' => $removedUser,
169						'group' => $group
170					]
171				);
172				$hasChanged = true;
173			}
174			foreach (array_diff($actualUsers, $knownUsers) as $addedUser) {
175				$userObject = $this->userManager->get($addedUser);
176				if ($userObject instanceof IUser) {
177					$this->dispatcher->dispatchTyped(new UserAddedEvent($groupObject, $userObject));
178				}
179				$this->logger->info(
180					'bgJ "updateGroups" – {user} added to {group}',
181					[
182						'app' => 'user_ldap',
183						'user' => $addedUser,
184						'group' => $group
185					]
186				);
187				$hasChanged = true;
188			}
189			if ($hasChanged) {
190				$qb->setParameters([
191					'members' => serialize($actualUsers),
192					'groupId' => $group
193				]);
194				$qb->execute();
195			}
196		}
197		$this->logger->debug(
198			'bgJ "updateGroups" – FINISHED dealing with known Groups.',
199			['app' => 'user_ldap']
200		);
201	}
202
203	/**
204	 * @param string[] $createdGroups
205	 */
206	private function handleCreatedGroups($createdGroups) {
207		$this->logger->debug(
208			'bgJ "updateGroups" – dealing with created Groups.',
209			['app' => 'user_ldap']
210		);
211
212		$query = $this->dbc->getQueryBuilder();
213		$query->insert('ldap_group_members')
214			->setValue('owncloudname', $query->createParameter('owncloudname'))
215			->setValue('owncloudusers', $query->createParameter('owncloudusers'));
216		foreach ($createdGroups as $createdGroup) {
217			$this->logger->info(
218				'bgJ "updateGroups" – new group "' . $createdGroup . '" found.',
219				['app' => 'user_ldap']
220			);
221			$users = serialize($this->groupBackend->usersInGroup($createdGroup));
222
223			$query->setParameter('owncloudname', $createdGroup)
224				->setParameter('owncloudusers', $users);
225			$query->execute();
226		}
227		$this->logger->debug(
228			'bgJ "updateGroups" – FINISHED dealing with created Groups.',
229			['app' => 'user_ldap']
230		);
231	}
232
233	/**
234	 * @param string[] $removedGroups
235	 */
236	private function handleRemovedGroups($removedGroups) {
237		$this->logger->debug(
238			'bgJ "updateGroups" – dealing with removed groups.',
239			['app' => 'user_ldap']
240		);
241
242		$query = $this->dbc->getQueryBuilder();
243		$query->delete('ldap_group_members')
244			->where($query->expr()->eq('owncloudname', $query->createParameter('owncloudname')));
245
246		foreach ($removedGroups as $removedGroup) {
247			$this->logger->info(
248				'bgJ "updateGroups" – group "' . $removedGroup . '" was removed.',
249				['app' => 'user_ldap']
250			);
251			$query->setParameter('owncloudname', $removedGroup);
252			$query->execute();
253		}
254		$this->logger->debug(
255			'bgJ "updateGroups" – FINISHED dealing with removed groups.',
256			['app' => 'user_ldap']
257		);
258	}
259}
260