Completed
Push — master ( 03449d...db6361 )
by Lukas
44s queued 24s
created

DefaultTokenProvider::invalidateTokenById()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
	 * Save the updated token
96
	 *
97
	 * @param IToken $token
98
	 */
99
	public function updateToken(IToken $token) {
100
		if (!($token instanceof DefaultToken)) {
101
			throw new InvalidTokenException();
102
		}
103
		$this->mapper->update($token);
104
	}
105
106
	/**
107
	 * Update token activity timestamp
108
	 *
109
	 * @throws InvalidTokenException
110
	 * @param IToken $token
111
	 */
112
	public function updateTokenActivity(IToken $token) {
113
		if (!($token instanceof DefaultToken)) {
114
			throw new InvalidTokenException();
115
		}
116
		/** @var DefaultToken $token */
117
		$now = $this->time->getTime();
118
		if ($token->getLastActivity() < ($now - 60)) {
119
			// Update token only once per minute
120
			$token->setLastActivity($now);
121
			$this->mapper->update($token);
122
		}
123
	}
124
125
	/**
126
	 * Get all token of a user
127
	 *
128
	 * The provider may limit the number of result rows in case of an abuse
129
	 * where a high number of (session) tokens is generated
130
	 *
131
	 * @param IUser $user
132
	 * @return IToken[]
133
	 */
134
	public function getTokenByUser(IUser $user) {
135
		return $this->mapper->getTokenByUser($user);
136
	}
137
138
	/**
139
	 * Get a token by token id
140
	 *
141
	 * @param string $tokenId
142
	 * @throws InvalidTokenException
143
	 * @return DefaultToken
144
	 */
145
	public function getToken($tokenId) {
146
		try {
147
			return $this->mapper->getToken($this->hashToken($tokenId));
148
		} catch (DoesNotExistException $ex) {
149
			throw new InvalidTokenException();
150
		}
151
	}
152
153
	/**
154
	 * @param IToken $savedToken
155
	 * @param string $tokenId session token
156
	 * @throws InvalidTokenException
157
	 * @throws PasswordlessTokenException
158
	 * @return string
159
	 */
160
	public function getPassword(IToken $savedToken, $tokenId) {
161
		$password = $savedToken->getPassword();
162
		if (is_null($password)) {
163
			throw new PasswordlessTokenException();
164
		}
165
		return $this->decryptPassword($password, $tokenId);
166
	}
167
168
	/**
169
	 * Encrypt and set the password of the given token
170
	 *
171
	 * @param IToken $token
172
	 * @param string $tokenId
173
	 * @param string $password
174
	 * @throws InvalidTokenException
175
	 */
176
	public function setPassword(IToken $token, $tokenId, $password) {
177
		if (!($token instanceof DefaultToken)) {
178
			throw new InvalidTokenException();
179
		}
180
		/** @var DefaultToken $token */
181
		$token->setPassword($this->encryptPassword($password, $tokenId));
182
		$this->mapper->update($token);
183
	}
184
185
	/**
186
	 * Invalidate (delete) the given session token
187
	 *
188
	 * @param string $token
189
	 */
190
	public function invalidateToken($token) {
191
		$this->mapper->invalidate($this->hashToken($token));
192
	}
193
194
	/**
195
	 * Invalidate (delete) the given token
196
	 *
197
	 * @param IUser $user
198
	 * @param int $id
199
	 */
200
	public function invalidateTokenById(IUser $user, $id) {
201
		$this->mapper->deleteById($user, $id);
202
	}
203
204
	/**
205
	 * Invalidate (delete) old session tokens
206
	 */
207
	public function invalidateOldTokens() {
208
		$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
209
		$this->logger->info('Invalidating tokens older than ' . date('c', $olderThan));
210
		$this->mapper->invalidateOld($olderThan);
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