Completed
Push — master ( 1452b7...195fc5 )
by Christoph
14:24 queued 11s
created

DefaultTokenProvider::setPassword()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 8
rs 9.4285
1
<?php
2
/**
3
 * @author Christoph Wurst <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2016, ownCloud, Inc.
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OC\Authentication\Token;
23
24
use Exception;
25
use OC\Authentication\Exceptions\InvalidTokenException;
26
use OC\Authentication\Exceptions\PasswordlessTokenException;
27
use OCP\AppFramework\Db\DoesNotExistException;
28
use OCP\AppFramework\Utility\ITimeFactory;
29
use OCP\IConfig;
30
use OCP\ILogger;
31
use OCP\IUser;
32
use OCP\Security\ICrypto;
33
34
class DefaultTokenProvider implements IProvider {
35
36
	/** @var DefaultTokenMapper */
37
	private $mapper;
38
39
	/** @var ICrypto */
40
	private $crypto;
41
42
	/** @var IConfig */
43
	private $config;
44
45
	/** @var ILogger $logger */
46
	private $logger;
47
48
	/** @var ITimeFactory $time */
49
	private $time;
50
51
	/**
52
	 * @param DefaultTokenMapper $mapper
53
	 * @param ICrypto $crypto
54
	 * @param IConfig $config
55
	 * @param ILogger $logger
56
	 * @param ITimeFactory $time
57
	 */
58
	public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger, ITimeFactory $time) {
59
		$this->mapper = $mapper;
60
		$this->crypto = $crypto;
61
		$this->config = $config;
62
		$this->logger = $logger;
63
		$this->time = $time;
64
	}
65
66
	/**
67
	 * Create and persist a new token
68
	 *
69
	 * @param string $token
70
	 * @param string $uid
71
	 * @param string $loginName
72
	 * @param string|null $password
73
	 * @param string $name
74
	 * @param int $type token type
75
	 * @return IToken
76
	 */
77
	public function generateToken($token, $uid, $loginName, $password, $name, $type = IToken::TEMPORARY_TOKEN) {
78
		$dbToken = new DefaultToken();
79
		$dbToken->setUid($uid);
80
		$dbToken->setLoginName($loginName);
81
		if (!is_null($password)) {
82
			$dbToken->setPassword($this->encryptPassword($password, $token));
83
		}
84
		$dbToken->setName($name);
85
		$dbToken->setToken($this->hashToken($token));
86
		$dbToken->setType($type);
87
		$dbToken->setLastActivity($this->time->getTime());
88
89
		$this->mapper->insert($dbToken);
90
91
		return $dbToken;
92
	}
93
94
	/**
95
	 * Update token activity timestamp
96
	 *
97
	 * @throws InvalidTokenException
98
	 * @param IToken $token
99
	 */
100
	public function updateToken(IToken $token) {
101
		if (!($token instanceof DefaultToken)) {
102
			throw new InvalidTokenException();
103
		}
104
		/** @var DefaultToken $token */
105
		$token->setLastActivity($this->time->getTime());
106
107
		$this->mapper->update($token);
108
	}
109
110
	/**
111
	 * Get all token of a user
112
	 *
113
	 * The provider may limit the number of result rows in case of an abuse
114
	 * where a high number of (session) tokens is generated
115
	 *
116
	 * @param IUser $user
117
	 * @return IToken[]
118
	 */
119
	public function getTokenByUser(IUser $user) {
120
		return $this->mapper->getTokenByUser($user);
121
	}
122
123
	/**
124
	 * Get a token by token id
125
	 *
126
	 * @param string $tokenId
127
	 * @throws InvalidTokenException
128
	 * @return DefaultToken
129
	 */
130
	public function getToken($tokenId) {
131
		try {
132
			return $this->mapper->getToken($this->hashToken($tokenId));
133
		} catch (DoesNotExistException $ex) {
134
			throw new InvalidTokenException();
135
		}
136
	}
137
138
	/**
139
	 * @param IToken $savedToken
140
	 * @param string $tokenId session token
141
	 * @throws InvalidTokenException
142
	 * @throws PasswordlessTokenException
143
	 * @return string
144
	 */
145
	public function getPassword(IToken $savedToken, $tokenId) {
146
		$password = $savedToken->getPassword();
147
		if (is_null($password)) {
148
			throw new PasswordlessTokenException();
149
		}
150
		return $this->decryptPassword($password, $tokenId);
151
	}
152
153
	/**
154
	 * Encrypt and set the password of the given token
155
	 *
156
	 * @param IToken $token
157
	 * @param string $tokenId
158
	 * @param string $password
159
	 * @throws InvalidTokenException
160
	 */
161
	public function setPassword(IToken $token, $tokenId, $password) {
162
		if (!($token instanceof DefaultToken)) {
163
			throw new InvalidTokenException();
164
		}
165
		/** @var DefaultToken $token */
166
		$token->setPassword($this->encryptPassword($password, $tokenId));
167
		$this->mapper->update($token);
168
	}
169
170
	/**
171
	 * Invalidate (delete) the given session token
172
	 *
173
	 * @param string $token
174
	 */
175
	public function invalidateToken($token) {
176
		$this->mapper->invalidate($this->hashToken($token));
177
	}
178
179
	/**
180
	 * Invalidate (delete) the given token
181
	 *
182
	 * @param IUser $user
183
	 * @param int $id
184
	 */
185
	public function invalidateTokenById(IUser $user, $id) {
186
		$this->mapper->deleteById($user, $id);
187
	}
188
189
	/**
190
	 * Invalidate (delete) old session tokens
191
	 */
192
	public function invalidateOldTokens() {
193
		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
194
		$this->logger->info('Invalidating tokens older than ' . date('c', $olderThan));
195
		$this->mapper->invalidateOld($olderThan);
196
	}
197
198
	/**
199
	 * @param string $token
200
	 * @throws InvalidTokenException
201
	 * @return DefaultToken user UID
202
	 */
203
	public function validateToken($token) {
204
		try {
205
			$dbToken = $this->mapper->getToken($this->hashToken($token));
206
			$this->logger->debug('valid default token for ' . $dbToken->getUID());
207
			return $dbToken;
208
		} catch (DoesNotExistException $ex) {
209
			throw new InvalidTokenException();
210
		}
211
	}
212
213
	/**
214
	 * @param string $token
215
	 * @return string
216
	 */
217
	private function hashToken($token) {
218
		$secret = $this->config->getSystemValue('secret');
219
		return hash('sha512', $token . $secret);
220
	}
221
222
	/**
223
	 * Encrypt the given password
224
	 *
225
	 * The token is used as key
226
	 *
227
	 * @param string $password
228
	 * @param string $token
229
	 * @return string encrypted password
230
	 */
231
	private function encryptPassword($password, $token) {
232
		$secret = $this->config->getSystemValue('secret');
233
		return $this->crypto->encrypt($password, $token . $secret);
234
	}
235
236
	/**
237
	 * Decrypt the given password
238
	 *
239
	 * The token is used as key
240
	 *
241
	 * @param string $password
242
	 * @param string $token
243
	 * @throws InvalidTokenException
244
	 * @return string the decrypted key
245
	 */
246
	private function decryptPassword($password, $token) {
247
		$secret = $this->config->getSystemValue('secret');
248
		try {
249
			return $this->crypto->decrypt($password, $token . $secret);
250
		} catch (Exception $ex) {
251
			// Delete the invalid token
252
			$this->invalidateToken($token);
253
			throw new InvalidTokenException();
254
		}
255
	}
256
257
}
258