Completed
Push — master ( 9b9ca0...f3dbfd )
by Lukas
13:11
created

MemoryCache::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Lukas Reschke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
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
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OC\Security\RateLimiting\Backend;
23
24
use OCP\AppFramework\Utility\ITimeFactory;
25
use OCP\ICache;
26
use OCP\ICacheFactory;
27
28
/**
29
 * Class MemoryCache uses the configured distributed memory cache for storing
30
 * rate limiting data.
31
 *
32
 * @package OC\Security\RateLimiting\Backend
33
 */
34
class MemoryCache implements IBackend {
35
	/** @var ICache */
36
	private $cache;
37
	/** @var ITimeFactory */
38
	private $timeFactory;
39
40
	/**
41
	 * @param ICacheFactory $cacheFactory
42
	 * @param ITimeFactory $timeFactory
43
	 */
44
	public function __construct(ICacheFactory $cacheFactory,
45
								ITimeFactory $timeFactory) {
46
		$this->cache = $cacheFactory->create(__CLASS__);
47
		$this->timeFactory = $timeFactory;
48
	}
49
50
	/**
51
	 * @param string $methodIdentifier
52
	 * @param string $userIdentifier
53
	 * @return string
54
	 */
55
	private function hash($methodIdentifier,
56
						  $userIdentifier) {
57
		return hash('sha512', $methodIdentifier . $userIdentifier);
58
	}
59
60
	/**
61
	 * @param string $identifier
62
	 * @return array
63
	 */
64
	private function getExistingAttempts($identifier) {
65
		$cachedAttempts = json_decode($this->cache->get($identifier), true);
66
		if(is_array($cachedAttempts)) {
67
			return $cachedAttempts;
68
		}
69
70
		return [];
71
	}
72
73
	/**
74
	 * {@inheritDoc}
75
	 */
76
	public function getAttempts($methodIdentifier,
77
								$userIdentifier,
78
								$seconds) {
79
		$identifier = $this->hash($methodIdentifier, $userIdentifier);
80
		$existingAttempts = $this->getExistingAttempts($identifier);
81
82
		$count = 0;
83
		$currentTime = $this->timeFactory->getTime();
84
		/** @var array $existingAttempts */
85
		foreach ($existingAttempts as $attempt) {
86
			if(($attempt + $seconds) > $currentTime) {
87
				$count++;
88
			}
89
		}
90
91
		return $count;
92
	}
93
94
	/**
95
	 * {@inheritDoc}
96
	 */
97
	public function registerAttempt($methodIdentifier,
98
									$userIdentifier,
99
									$period) {
100
		$identifier = $this->hash($methodIdentifier, $userIdentifier);
101
		$existingAttempts = $this->getExistingAttempts($identifier);
102
		$currentTime = $this->timeFactory->getTime();
103
104
		// Unset all attempts older than $period
105
		foreach ($existingAttempts as $key => $attempt) {
106
			if(($attempt + $period) < $currentTime) {
107
				unset($existingAttempts[$key]);
108
			}
109
		}
110
		$existingAttempts = array_values($existingAttempts);
111
112
		// Store the new attempt
113
		$existingAttempts[] = (string)$currentTime;
114
		$this->cache->set($identifier, json_encode($existingAttempts));
115
	}
116
}
117