/
Access.php
304 lines (271 loc) · 10.5 KB
/
Access.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<?php
namespace infrajs\access;
use infrajs\mem\Mem;
use infrajs\nostore\Nostore;
use infrajs\cache\Cache;
use infrajs\view\View;
use infrajs\path\Path;
class Access {
public static $conf = array(
"test"=>array("127.0.0.1","::1"),
"debug"=>array("127.0.0.1","::1"),
"admin"=>array('login'=>"admin", 'password'=>"admin")
);
public static function isTest()
{
if (self::isDebug()) return true;
$conf = static::$conf;
$ips = $conf['test'];
if (is_array($ips)) {
$is = in_array($_SERVER['REMOTE_ADDR'], $ips);
} elseif (is_string($ips)) {
$is = ($_SERVER['REMOTE_ADDR'] == $ips);
} else {
$is = !!$ips;
}
return $is;
}
public static function isDebug()
{
if (self::isAdmin()) return true;
$conf = static::$conf;
$ips = $conf['debug'];
if (is_array($ips)) {
$is = in_array($_SERVER['REMOTE_ADDR'], $ips);
} elseif (is_string($ips)) {
$is = ($_SERVER['REMOTE_ADDR'] == $ips);
} else {
$is = !!$ips;
}
return $is;
}
/**
* Активируем зависимость текущего кода
* от того для кого этот код работает
**/
public static function nostore($is) {
Nostore::on();
}
public static function test($die = false)
{
$is = self::isTest();
//Тестировщик Никак не влияет на кэш
if (!$die) return $is;
if ($is) return;
header('HTTP/1.0 403 Forbidden');
die('{"msg":"Недостаточно прав для доступа. Требуется config.access.test:['.$_SERVER['REMOTE_ADDR'].']"}');
}
public static function debug($die = false)
{
$is = self::isDebug();
Access::nostore($is);
if (!$die) return $is;
if ($is) return;
header('HTTP/1.0 403 Forbidden');
die('{"msg":"Недостаточно прав для доступа. Требуется config.access.debug:['.$_SERVER['REMOTE_ADDR'].']"}');
}
public static function headers() {
if (Access::isTest()) {
//error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
//ini_set('display_errors', 1);
header('Infrajs-Test:true');
} else {
//error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
header('Infrajs-Test:false');
//ini_set('display_errors', 0);
}
if (Access::isDebug()) {
header('Infrajs-Debug:true');
Nostore::on(); //Браузер не кэширует no-store.
} else {
header('Infrajs-Debug:false');
}
if (Access::isAdmin()) {
header('Infrajs-Admin:true');
Access::adminSetTime();
} else {
header('Infrajs-Admin:false');
}
}
/**
* Тихая функция, только проверка, без отметок.
*/
public static function isAdmin()
{
if(!Path::theme('~.infra.json')) {
return false;
//echo '<pre>';
//throw new \Exception('Для авторизации требуется создать конфигурационный файл ~.infra.json с данными {"admin": {"login":"admin", "password":"admin"}}');
}
$conf = static::$conf;
$data = $conf['admin'];
if(empty($data['login'])) return false;
if(empty($data['password'])) return false;
$_ADM_NAME = $data['login'];
$_ADM_PASS = $data['password'];
if (empty($_SERVER['HTTP_USER_AGENT'])) {
$_SERVER['HTTP_USER_AGENT'] = '';
}
$realkey = md5($_ADM_NAME.$_ADM_PASS.$_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR']);
$key = View::getCookie('infra_admin');
return ($key === $realkey);
}
/**
* Access::admin(true) - пропускает только если ты администратор, иначе выкидывает окно авторизации
* Access::admin(false) - пропускает только если ты НЕ администратор, иначе выкидывает окно авторизации
* $ans выводится в json если нажать отмена
* Access::admin(array('login','pass'));.
*/
public static function admin($break = null, $ans = array('msg' => 'Требуется авторизация', 'result' => 0))
{
$data = static::$conf['admin'];
if(empty($data['login']) || empty($data['password'])) return false;
$_ADM_NAME = $data['login'];
$_ADM_PASS = $data['password'];
$admin = null;//Неизвестно
$realkey = md5($_ADM_NAME.$_ADM_PASS.$_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR']);
if (is_array($break)) {
Nostore::on();
//Если имя в конфиге указано, и переданные данные в массиве соответствуют
$admin = ($_ADM_NAME && $break[0] === $_ADM_NAME && $break[1] === $_ADM_PASS);
if ($admin) {
View::setCookie('infra_admin', $realkey);
} else {
View::setCookie('infra_admin');
}
} else {
$key = View::getCookie('infra_admin');
$admin = ($key === $realkey);
if ($break === false) {
Nostore::on();
View::setCookie('infra_admin');
$admin = false;
} elseif ($break === true && !$admin) {
Nostore::on();
//Если имя в конфиге указано, и переданные данные по HTTP соответствуют
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
$admin = ($_ADM_NAME && $_SERVER['PHP_AUTH_USER'] == $_ADM_NAME && $_SERVER['PHP_AUTH_PW'] == $_ADM_PASS);
} else {
$admin = false;
}
if ($admin) {
View::setCookie('infra_admin', $realkey);
} else {
header('WWW-Authenticate: Basic realm="Protected Area"');
header('HTTP/1.0 401 Unauthorized');
echo json_encode($ans, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
exit;
}
} else {
Access::nostore($admin);//Иногда сохраняем кэш иногда нет. Админ всегда проверяет кэш. А данный вызов добавляет проверку которая возвращает всегда true для админа
}
}
return $admin;
}
public static function adminSetTime($t = null)
{
if (is_null($t)) $t = time();
Access::$time = $t;
$adm = array('time' => $t);
Mem::set('infra_admin_time', $adm);
}
/**
* Отвечает на вопрос! Время настало для сложной обработки?
* Функция стремится сказать что время ещё не пришло... и последней инстанцией будет выполнение фукции которая должна вернуть true или false
* Если передать метку времени и функцию это будет означать запуск функции если метка времени старей админской метки
* В debug режиме функция запускается всегда.
*/
public static function adminIsTime($cachetime = 0, $callback = false, $re = false)
{
if (!$cachetime || $re) {
return true; //Нет кэша... пришло всремя для сложной обработки
};
//Мы тут не меняем содержание.. а только отвечам на вопрос можно ли закэшировать... cache_no от Infra_debug тут неуместен
if (self::isDebug() || $cachetime < self::adminTime()) {
if ($callback) {
return !!$callback($cachetime); //Только функция сможет сказать надо или нет
}
return true;
}
return false;
}
public static function getDebugTime()
{
if (Access::isDebug()) return time();
else return Access::adminTime();
}
public static $time = false;
/**
* Время когда админ что-то сделал (время последнего обращения к функции infra_admin и её результате true)
* Функция работает без параметров...возвращает дату последних изменений админа для всей системы
*/
public static function getAdminTime()
{
if (Access::isAdmin()) return time();
return Access::adminTime();
}
public static function adminTime()
{
if (Access::$time === false) {
$adm = Mem::get('infra_admin_time');
if (!$adm) {
$adm = array();
}
if (!isset($adm['time'])) {
$adm['time'] = 0;
}
$update_time = Access::updateTime();
Access::$time = max($update_time, $adm['time']);
}
return Access::$time;
}
public static $update_time = false;
public static function updateTime()
{
if (Access::$update_time === false) {
$update_time = filemtime(__FILE__);
if (is_file('.git/index')) {
$update_time = max($update_time, filemtime('.git/index'));
}
if (is_file('composer.lock')) {
$update_time = max($update_time, filemtime('composer.lock'));
}
Access::$update_time = $update_time;
}
return Access::$update_time;
}
public static function cache($name, $fn, $args = array()) {
$re = Access::isDebug();
return Cache::exec( [true], 'Access::cache'.$name, $fn, $args, $re);
//}, [$name, $args],['infrajs\\access\\Access','adminTime'],[], 1);
}
public static function modified($etag = '')
{
//$v изменение которой должно создавать новую копию кэша
if (self::isDebug()) return;
/*if ($etag) {
//Мы осознано включаем возможность кэшировать, даже если были запреты до этого!
//так ак есть Etag и в нём срыты эти неявные условия
//Таким образом отменяется обращение к базе даных, инициализация сессии и тп.
Nostore::off();
Если так то вручную надо выставлять Nostore::off(); мы не знаем что там за etag
}*/
if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$last_modified = self::adminTime();
/*
Warning: strtotime(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone
*/
if (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) > $last_modified) {
if (empty($_SERVER['HTTP_IF_NONE_MATCH']) || $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
//header('ETag: '.$etag);
//header('Last-Modified: '.$_SERVER['HTTP_IF_MODIFIED_SINCE']);
header('HTTP/1.0 304 Not Modified');
exit;
}
}
}
header('ETag: '.$etag);
$now = gmdate('D, d M Y H:i:s', time()).' GMT';
header('Last-Modified: '.$now);
}
}