1<?php
2/**
3 * @copyright Copyright (c) 2015, ownCloud, Inc.
4 *
5 * @author blizzz <blizzz@arthur-schiwon.de>
6 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
7 * @author Joas Schilling <coding@schilljs.com>
8 * @author Lukas Reschke <lukas@statuscode.ch>
9 * @author Morris Jobke <hey@morrisjobke.de>
10 * @author Robin Appelman <robin@icewind.nl>
11 * @author Roeland Jago Douma <roeland@famdouma.nl>
12 *
13 * @license AGPL-3.0
14 *
15 * This code is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Affero General Public License, version 3,
17 * as published by the Free Software Foundation.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public License, version 3,
25 * along with this program. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28namespace OCA\Files_External\Lib\Auth\Password;
29
30use OCA\Files_External\Lib\Auth\AuthMechanism;
31use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
32use OCA\Files_External\Lib\StorageConfig;
33use OCA\Files_External\Listener\StorePasswordListener;
34use OCP\Authentication\Exceptions\CredentialsUnavailableException;
35use OCP\Authentication\LoginCredentials\IStore as CredentialsStore;
36use OCP\EventDispatcher\IEventDispatcher;
37use OCP\IL10N;
38use OCP\ISession;
39use OCP\IUser;
40use OCP\IUserBackend;
41use OCP\LDAP\ILDAPProviderFactory;
42use OCP\Security\ICredentialsManager;
43use OCP\User\Events\PasswordUpdatedEvent;
44use OCP\User\Events\UserLoggedInEvent;
45
46/**
47 * Username and password from login credentials, saved in DB
48 */
49class LoginCredentials extends AuthMechanism {
50	public const CREDENTIALS_IDENTIFIER = 'password::logincredentials/credentials';
51
52	/** @var ISession */
53	protected $session;
54
55	/** @var ICredentialsManager */
56	protected $credentialsManager;
57
58	/** @var CredentialsStore */
59	private $credentialsStore;
60
61	/** @var ILDAPProviderFactory */
62	private $ldapFactory;
63
64	public function __construct(
65		IL10N $l,
66		ISession $session,
67		ICredentialsManager $credentialsManager,
68		CredentialsStore $credentialsStore,
69		IEventDispatcher $eventDispatcher,
70		ILDAPProviderFactory $ldapFactory
71	) {
72		$this->session = $session;
73		$this->credentialsManager = $credentialsManager;
74		$this->credentialsStore = $credentialsStore;
75		$this->ldapFactory = $ldapFactory;
76
77		$this
78			->setIdentifier('password::logincredentials')
79			->setScheme(self::SCHEME_PASSWORD)
80			->setText($l->t('Log-in credentials, save in database'))
81			->addParameters([
82			]);
83
84		$eventDispatcher->addServiceListener(UserLoggedInEvent::class, StorePasswordListener::class);
85		$eventDispatcher->addServiceListener(PasswordUpdatedEvent::class, StorePasswordListener::class);
86	}
87
88	private function getCredentials(IUser $user): array {
89		$credentials = $this->credentialsManager->retrieve($user->getUID(), self::CREDENTIALS_IDENTIFIER);
90
91		if (is_null($credentials)) {
92			// nothing saved in db, try to get it from the session and save it
93			try {
94				$sessionCredentials = $this->credentialsStore->getLoginCredentials();
95
96				if ($sessionCredentials->getUID() !== $user->getUID()) {
97					// Can't take the credentials from the session as they are not the same user
98					throw new CredentialsUnavailableException();
99				}
100
101				$credentials = [
102					'user' => $sessionCredentials->getLoginName(),
103					'password' => $sessionCredentials->getPassword(),
104				];
105
106				$this->credentialsManager->store($user->getUID(), self::CREDENTIALS_IDENTIFIER, $credentials);
107			} catch (CredentialsUnavailableException $e) {
108				throw new InsufficientDataForMeaningfulAnswerException('No login credentials saved');
109			}
110		}
111
112		return $credentials;
113	}
114
115	public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
116		if (!isset($user)) {
117			throw new InsufficientDataForMeaningfulAnswerException('No login credentials saved');
118		}
119		$credentials = $this->getCredentials($user);
120
121		$loginKey = $storage->getBackendOption("login_ldap_attr");
122		if ($loginKey) {
123			$backend = $user->getBackend();
124			if ($backend instanceof IUserBackend && $backend->getBackendName() === 'LDAP') {
125				$value = $this->getLdapPropertyForUser($user, $loginKey);
126				if ($value === null) {
127					throw new InsufficientDataForMeaningfulAnswerException('Custom ldap attribute not set for user ' . $user->getUID());
128				}
129				$storage->setBackendOption('user', $value);
130			} else {
131				throw new InsufficientDataForMeaningfulAnswerException('Custom ldap attribute configured but user ' . $user->getUID() . ' is not an ldap user');
132			}
133		} else {
134			$storage->setBackendOption('user', $credentials['user']);
135		}
136		$storage->setBackendOption('password', $credentials['password']);
137	}
138
139	private function getLdapPropertyForUser(IUser $user, string $property): ?string {
140		return $this->ldapFactory->getLDAPProvider()->getUserAttribute($user->getUID(), $property);
141	}
142}
143