Passed
Pull Request — master (#197)
by
unknown
27:39
created

RestServer::getRestNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 4
c 1
b 0
f 1
dl 0
loc 5
ccs 5
cts 5
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Ubiquity\controllers\rest;
4
5
use Ubiquity\controllers\Startup;
6
use Ubiquity\cache\ClassUtils;
7
use Ubiquity\cache\CacheManager;
8
use Ubiquity\exceptions\RestException;
9
use Ubiquity\log\Logger;
10
use Ubiquity\utils\http\URequest;
11
12
/**
13
 * Rest server base class.
14
 * Ubiquity\controllers\rest$RestServer
15
 * This class is part of Ubiquity
16
 *
17
 * @author jcheron <[email protected]>
18
 * @version 1.0.8
19
 *
20
 */
21
class RestServer {
22
	/**
23
	 *
24
	 * @var array
25
	 */
26
	protected $config;
27
	protected $headers;
28
	protected $tokensFolder;
29
	protected $tokenLength;
30
	protected $tokenDuration;
31
	protected $tokensCacheKey = '_apiTokens';
32
	protected $allowedOrigins;
33
34
	/**
35
	 *
36
	 * @var ApiTokens
37
	 */
38
	protected $apiTokens;
39
40 13
	public function __construct(&$config, $headers = null) {
41 13
		$this->config = $config;
42 13
		$this->headers = [ 'Access-Control-Allow-Origin' => '*','Access-Control-Allow-Credentials' => 'true','Access-Control-Max-Age' => '86400','Access-Control-Allow-Methods' => 'GET, POST, OPTIONS, PUT, DELETE, PATCH, HEAD','Content-Type' => 'application/json; charset=utf8' ];
43 13
		if (\is_array ( $headers )) {
44
			$this->headers = \array_merge ( $this->headers, $headers );
45
		}
46
	}
47
48
49
	private function tokenCallback($callback){
50
		if (! isset ( $this->apiTokens )) {
51
			$this->apiTokens = $this->_loadApiTokens ();
52
		}
53
		$token = $callback();
54
		$this->_addHeaderToken ( $token );
55
		return [ 'access_token' => $token,'token_type' => 'Bearer','expires_in' => $this->apiTokens->getDuration () ];
56
	}
57
	/**
58
	 * Establishes the connection with the server, returns an added token in the Authorization header of the request
59
	 *
60
	 * @return array
61
	 */
62
	public function connect($datas=null) {
63
		return $this->tokenCallback(function() use ($datas) {
64
			return $this->apiTokens->addToken ($datas);
65
		});
66
	}
67
68
	/**
69
	 * Refresh an active token
70
	 * @return array
71
	 * @throws RestException
72
	 */
73
	public function refreshToken(): array {
74
		return $this->tokenCallback(function() {
75
			$key=$this->_getHeaderToken();
76
			return $this->apiTokens->refreshToken($key);
77
		});
78
	}
79
80
	/**
81
	 * Check if token is valid
82
	 * @param callable $callback
83
	 * @return boolean
84
	 * @throws RestException
85
	 */
86 1
	public function isValid($callback) {
87 1
		$this->apiTokens = $this->_loadApiTokens ();
88 1
		$key = $this->_getHeaderToken ();
89
		if ($this->apiTokens->isExpired ( $key )) {
90
			return false;
91
		} else {
92
			$token=$this->apiTokens->getToken($key);
93
			if($callback($token['datas']??null)) {
94
				$this->_addHeaderToken($key);
95
				return true;
96
			}
97
			return false;
98
		}
99
	}
100
101 1
	public function _getHeaderToken() {
102 1
		$authHeader = $this->_getHeader ( 'Authorization' );
103 1
		if ($authHeader !== false) {
104
			$headerDatas = \explode ( ' ', $authHeader, 2 );
105
			if (\count( $headerDatas ) === 2) {
106
				list ( $type, $data ) = $headerDatas;
107
				if (\strcasecmp ( $type, 'Bearer' ) == 0) {
108
					return $data;
109
				} else {
110
					throw new RestException ( 'Bearer is required in authorization header.' );
111
				}
112
			} else {
113
				throw new RestException ( 'The header Authorization is required in http headers.' );
114
			}
115
		} else {
116 1
			throw new RestException ( 'The header Authorization is required in http headers.' );
117
		}
118
	}
119
120 13
	public function finalizeTokens() {
121 13
		if (isset ( $this->apiTokens )) {
122
			$this->apiTokens->removeExpireds ();
123
			$this->apiTokens->storeToCache ();
124
		}
125
	}
126
127 1
	public function _getHeader($header) {
128 1
		$headers = getallheaders ();
129 1
		if (isset ( $headers [$header] )) {
130
			return $headers [$header];
131
		}
132 1
		return false;
133
	}
134
135
	public function _addHeaderToken($token) {
136
		$this->_header ( 'Authorization', 'Bearer ' . $token, true );
137
	}
138
139 1
	public function _loadApiTokens() {
140 1
		return $this->getApiTokens ()->getFromCache ( CacheManager::getAbsoluteCacheDirectory () . \DS, $this->tokensCacheKey );
141
	}
142
143 1
	protected function getApiTokens() {
144 1
		if (! isset ( $this->apiTokens )) {
145 1
			$this->apiTokens = $this->newApiTokens ();
146
		}
147 1
		return $this->apiTokens;
148
	}
149
150
	/**
151
	 * To override for defining another ApiToken type
152
	 *
153
	 * @return ApiTokens
154
	 */
155 1
	protected function newApiTokens() {
156 1
		return new ApiTokens ( $this->tokenLength, $this->tokenDuration );
157
	}
158
159 13
	protected function getAllowedOrigin() {
160 13
		$http_origin = URequest::getOrigin ();
161 13
		if (\is_array ( $this->allowedOrigins )) {
162
			if (\array_search ( $http_origin, $this->allowedOrigins ) !== false) {
163
				return $http_origin;
164
			}
165
			return 'null';
166
		}
167 13
		return '*';
168
	}
169
170 13
	protected function setAccessControlAllowOriginHeader() {
171 13
		$origin = $this->getAllowedOrigin ();
172 13
		unset ( $this->headers ['Access-Control-Allow-Origin'] );
173 13
		\header ( 'Access-Control-Allow-Origin: ' . $origin, true );
174
	}
175
176 13
	protected function addOtherHeaders() {
177 13
		foreach ( $this->headers as $k => $v ) {
178 13
			$this->_header ( $k, $v );
179
		}
180
	}
181
182
	/**
183
	 *
184
	 * @param string $headerField
185
	 * @param string $value
186
	 * @param boolean $replace
187
	 */
188 13
	public function _header($headerField, $value = null, bool $replace = true) {
189 13
		if (! isset ( $value )) {
190 13
			if (isset ( $this->headers [$headerField] )) {
191 13
				$value = $this->headers [$headerField];
192 13
				unset ( $this->headers [$headerField] );
193
			} else
194 13
				return;
195
		}
196 13
		\header ( \trim ( $headerField ) . ": " . \trim ( $value ), $replace );
197
	}
198
199
	/**
200
	 *
201
	 * @param string $contentType default application/json
202
	 * @param string $charset default utf8
203
	 */
204 13
	public function _setContentType($contentType = null, $charset = null) {
205 13
		$value = $contentType;
206 13
		if (isset ( $charset )){
207
			$value .= '; charset=' . $charset;
208
		}
209 13
		$this->_header ( 'Content-type', $value );
210
	}
211
212 13
	public function cors() {
213 13
		$this->setAccessControlAllowOriginHeader ();
214 13
		$this->_header ( 'Access-Control-Allow-Credentials' );
215 13
		$this->_header ( 'Access-Control-Max-Age' );
216 13
		if ($_SERVER ['REQUEST_METHOD'] == 'OPTIONS') {
217
			if (isset ( $_SERVER ['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] )){
218
				$this->_header ( 'Access-Control-Allow-Methods' );
219
			}
220
			if (isset ( $_SERVER ['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] )) {
221
				$this->_header ( 'Access-Control-Allow-Headers', $_SERVER ['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] );
222
			} else {
223
				$this->_header ( 'Access-Control-Allow-Headers', '*' );
224
			}
225
			Logger::info ( 'Rest', 'cors exit normally', 'Cors' );
226
		}
227 13
		$this->addOtherHeaders ();
228
	}
229
230 1
	public static function getRestNamespace() {
231 1
		$config = Startup::getConfig ();
232 1
		$controllerNS = Startup::getNS('controllers');
233 1
		$restNS = $config ['mvcNS']['rest']??"";
234 1
		return ClassUtils::getNamespaceFromParts ( [ $controllerNS,$restNS ] );
235
	}
236
237
	/**
238
	 * Adds an unique allowed origin for access control.
239
	 *
240
	 * @param string $address
241
	 */
242
	public function setAllowedOrigin($address = '*') {
243
		if ($address !== '*') {
244
			$this->allowedOrigins = [ $address ];
245
		} else {
246
			$this->allowedOrigins = [ ];
247
		}
248
	}
249
250
	/**
251
	 * Sets the allowed origins for access control.
252
	 *
253
	 * @param array $addresses
254
	 */
255
	public function setAllowedOrigins($addresses) {
256
		$this->allowedOrigins = $addresses;
257
	}
258
259
	/**
260
	 * Adds an allowed origin for access control.
261
	 *
262
	 * @param string $address
263
	 */
264
	public function addAllowedOrigin($address) {
265
		$this->allowedOrigins = [ $address ];
266
	}
267
268
	/**
269
	 *
270
	 * @param int $tokenLength
271
	 */
272
	public function setTokenLength($tokenLength) {
273
		$this->tokenLength = $tokenLength;
274
	}
275
276
	/**
277
	 *
278
	 * @param mixed $tokenDuration
279
	 */
280
	public function setTokenDuration($tokenDuration) {
281
		$this->tokenDuration = $tokenDuration;
282
	}
283
}
284