MRBS 1.12.2 等保2.0二级整改完整提交
包含:登录失败锁定、90天密码有效期、30分钟会话超时、 强制改密、登录审计日志、屏幕水印、企业背景图、 备案信息固定底部、favicon、JS空集合保护、 会话过期体验优化(403 JSON)、display_errors 关闭、 固定 key 根治 Integrity check failed 等全部改动 注意:config.inc.php/.htaccess/.user.ini 含敏感信息, 通过 .gitignore 排除,勿推送到公开仓库。
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace MRBS;
|
||||
|
||||
use Countable;
|
||||
use DateTimeZone;
|
||||
use SeekableIterator;
|
||||
|
||||
/**
|
||||
* A class for handling a list of periods for a given area. It isolates from the rest of MRBS
|
||||
* the knowledge of how periods are represented in start and end times and how they are stored
|
||||
* in the database.
|
||||
*
|
||||
* Periods are represented as minutes from noon, ie 1200 is $period[0], 1201 $period[1], etc.
|
||||
*
|
||||
* They are stored in the database as JSON encoded arrays of period names and start and end times.
|
||||
*/
|
||||
class Periods implements Countable, SeekableIterator
|
||||
{
|
||||
private $area_id;
|
||||
private $tzid;
|
||||
private $index = 0;
|
||||
private $data = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param int|null $area_id The area ID, or null if not yet known. Creating an instance of this class without
|
||||
* an area can be useful when creating a new area.
|
||||
*/
|
||||
public function __construct(?int $area_id = null)
|
||||
{
|
||||
$this->area_id = $area_id;
|
||||
if (isset($area_id))
|
||||
{
|
||||
$this->tzid = get_area_timezone($area_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the periods for a given area from the database.
|
||||
*/
|
||||
public static function getForArea(int $area_id) : self
|
||||
{
|
||||
static $result = []; // Cache for performance
|
||||
|
||||
if (!isset($result[$area_id]))
|
||||
{
|
||||
$sql = "SELECT periods
|
||||
FROM " . _tbl('area') . "
|
||||
WHERE id=:id
|
||||
LIMIT 1";
|
||||
$res = db()->query($sql, [':id' => $area_id]);
|
||||
|
||||
if (false !== ($row = $res->next_row_keyed()))
|
||||
{
|
||||
$result[$area_id] = self::fromDbValue($area_id, $row['periods']);
|
||||
}
|
||||
}
|
||||
|
||||
return clone $result[$area_id];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a periods value as stored in the database to an instance of this class.
|
||||
*/
|
||||
public static function fromDbValue(int $area_id, string $value) : self
|
||||
{
|
||||
$result = new self($area_id);
|
||||
|
||||
$array = json_decode($value, true);
|
||||
// The periods are stored in the database as either:
|
||||
// (a) a simple array of period names (the old way of storing periods
|
||||
// which we handle for backwards compatibility); or
|
||||
// (b) an associative array of period names and start/end times.
|
||||
if (is_assoc($array))
|
||||
{
|
||||
foreach ($array as $period_name => $times)
|
||||
{
|
||||
$result->add(new Period($period_name, $times[0], $times[1]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($array as $period_name)
|
||||
{
|
||||
$result->add(new Period($period_name));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the object to a value suitable for storing in the database.
|
||||
*/
|
||||
public function toDbValue() : string
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->data as $period)
|
||||
{
|
||||
$result[$period->name] = [$period->start, $period->end];
|
||||
}
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the periods, checking that the start and end times are valid and in ascending order.
|
||||
*
|
||||
* @return true|string
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
foreach ($this->data as $i => $period)
|
||||
{
|
||||
// If we're not using times, everything is OK.
|
||||
if (($i === 0) && (!isset($period->start)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise check that the start and end times are valid and are in ascending order.
|
||||
try
|
||||
{
|
||||
if (false === ($start = DateTime::createFromFormat('H:i', $period->start)))
|
||||
{
|
||||
return get_vocab('invalid_period_start_time', $period->start, $period->name);
|
||||
}
|
||||
if (false === ($end = DateTime::createFromFormat('H:i', $period->end)))
|
||||
{
|
||||
return get_vocab('invalid_period_end_time', $period->end, $period->name);
|
||||
}
|
||||
if (isset($last_end_time) && ($start < $last_end_time))
|
||||
{
|
||||
return get_vocab('period_start_before_last_end', $period->name);
|
||||
}
|
||||
if ($start >= $end)
|
||||
{
|
||||
return get_vocab('period_must_have_positive_duration', $period->name);
|
||||
}
|
||||
$last_end_time = $end;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return get_vocab('invalid_period_time', $period->name);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function add(Period $period) : void
|
||||
{
|
||||
$this->data[] = $period;
|
||||
}
|
||||
|
||||
public function current() : Period
|
||||
{
|
||||
return $this->data[$this->index];
|
||||
}
|
||||
|
||||
public function next() : void
|
||||
{
|
||||
$this->index++;
|
||||
}
|
||||
|
||||
public function key(): int
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return isset($this->data[$this->index]);
|
||||
}
|
||||
|
||||
public function rewind() : void
|
||||
{
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
public function count() : int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
public function seek($offset) : void
|
||||
{
|
||||
if ($offset < 0 || $offset >= $this->count())
|
||||
{
|
||||
throw new \OutOfBoundsException("Invalid offset $offset");
|
||||
}
|
||||
$this->index = $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the object has start and end times defined.
|
||||
*/
|
||||
public function hasTimes() : bool
|
||||
{
|
||||
return ($this->count() > 0) && ($this->data[0]->start !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a period by its index. Note that the iterator key is unchanged.
|
||||
*/
|
||||
public function offsetGet($offset) : Period
|
||||
{
|
||||
if ($offset < 0 || $offset >= $this->count())
|
||||
{
|
||||
throw new \OutOfBoundsException("Invalid offset $offset");
|
||||
}
|
||||
return $this->data[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a period by its nominal start time in seconds. Note that the
|
||||
* iterator key is unchanged.
|
||||
*/
|
||||
public function offsetGetByNominalSeconds(int $seconds) : Period
|
||||
{
|
||||
return $this->offsetGet(self::nominalSecondsToIndex($seconds));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a period by its timestamp. Note that the iterator key is unchanged.
|
||||
*
|
||||
* @param bool $previous If true, return the previous period, otherwise return this period.
|
||||
*/
|
||||
public function offsetGetByTimestamp(int $timestamp, bool $previous=false) : Period
|
||||
{
|
||||
$index = $this->timestampToIndex($timestamp);
|
||||
if ($previous)
|
||||
{
|
||||
$index--;
|
||||
}
|
||||
// Make sure we're within bounds
|
||||
return $this->offsetGet(max(0, min($index, $this->count()-1)));
|
||||
}
|
||||
|
||||
|
||||
public static function getHourByOffset(int $offset) : int
|
||||
{
|
||||
return 12 + intval($offset/60);
|
||||
}
|
||||
|
||||
|
||||
public static function getMinuteByOffset(int $offset) : int
|
||||
{
|
||||
return $offset % 60;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the timestamp, as held in the entry table, of the start of the period at the given index on the given date.
|
||||
*/
|
||||
public function getStartTimestamp(int $index, DateTime $date) : int
|
||||
{
|
||||
return $date->setTime(12, 0)->getTimestamp() + ($index * 60);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the timestamp, as held in the entry table, of the end of the period at the given index on the given date.
|
||||
*/
|
||||
public function getEndTimestamp(int $index, DateTime $date) : int
|
||||
{
|
||||
return $this->getStartTimestamp($index, $date) + 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a period to a real end time.
|
||||
*
|
||||
* @param int $timestamp The timestamp of the period.
|
||||
*
|
||||
* @return false|int False if the time is invalid, otherwise the real end time as a Unix timestamp.
|
||||
*/
|
||||
public function timestampToRealEnd(int $timestamp) : int
|
||||
{
|
||||
return $this->getRealTime($timestamp, $this->offsetGetByTimestamp($timestamp)->end);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a period to a real start time.
|
||||
*
|
||||
* @param int $timestamp The timestamp of the period.
|
||||
*
|
||||
* @return false|int False if the time is invalid, otherwise the real start time as a Unix timestamp.
|
||||
*/
|
||||
public function timestampToRealStart(int $timestamp) : int
|
||||
{
|
||||
return $this->getRealTime($timestamp, $this->offsetGetByTimestamp($timestamp)->start);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a period start or end time to a real time.
|
||||
*
|
||||
* @param int $timestamp The timestamp of the period
|
||||
* @param string $hhmm The time in the format HH:MM
|
||||
*/
|
||||
private function getRealTime(int $timestamp, string $hhmm) : int
|
||||
{
|
||||
if (!isset($this->tzid))
|
||||
{
|
||||
throw new \Exception('No timezone set');
|
||||
}
|
||||
|
||||
return timestamp_set_time($timestamp, $hhmm, $this->tzid);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a period nominal start time in seconds (ie nominal seconds since
|
||||
* midnight, ignoring DST transitions) to an index into the periods array.
|
||||
*/
|
||||
private static function nominalSecondsToIndex(int $seconds) : int
|
||||
{
|
||||
// Periods are counted as minutes from noon, ie 1200 is $period[0],
|
||||
// 1201 $period[1], etc.
|
||||
return intval($seconds/60) - (12*60);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a timestamp to an index into the periods array.
|
||||
*/
|
||||
private function timestampToIndex(int $timestamp) : int
|
||||
{
|
||||
if (!isset($this->tzid))
|
||||
{
|
||||
throw new \Exception('No timezone set');
|
||||
}
|
||||
|
||||
$noon = new DateTime('now', new DateTimeZone($this->tzid));
|
||||
$noon->setTimestamp($timestamp);
|
||||
$noon->setTime(12, 0);
|
||||
return intval(($timestamp - $noon->getTimestamp())/60);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user