Rev 627 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
<?php
declare(strict_types = 1);
namespace LeadersLinked;
use Laminas\Db\Adapter\AdapterInterface;
use Laminas\ModuleManager\ModuleEvent;
use Laminas\ModuleManager\ModuleManager;
use Laminas\Mvc\MvcEvent;
use Laminas\Config\Reader\Ini;
use Laminas\Permissions\Acl\Acl;
use Laminas\Permissions\Acl\Role\GenericRole;
use LeadersLinked\Plugin\CurrentUserPlugin;
use LeadersLinked\Mapper\UserMapper;
use LeadersLinked\Authentication\AuthTokenAdapter;
use Laminas\Authentication\AuthenticationService;
use Laminas\Permissions\Acl\Resource\GenericResource;
use LeadersLinked\Model\UserType;
use LeadersLinked\Plugin\CurrentNetworkPlugin;
use LeadersLinked\Model\Network;
use LeadersLinked\Model\User;
use LeadersLinked\Mapper\CompanyUserMapper;
use LeadersLinked\Model\CompanyUser;
use LeadersLinked\Mapper\CompanyMapper;
use LeadersLinked\Mapper\CompanyServiceMapper;
use LeadersLinked\Model\Service;
use LeadersLinked\Library\Functions;
use LeadersLinked\Mapper\DailyPulseMapper;
use LeadersLinked\Model\DailyPulse;
use LeadersLinked\Mapper\OrganizationPositionMapper;
use LeadersLinked\Mapper\KnowledgeAreaCategoryJobDescriptionMapper;
use LeadersLinked\Mapper\MyCoachCategoryJobDescriptionMapper;
use LeadersLinked\Mapper\KnowledgeAreaCategoryUserMapper;
use LeadersLinked\Mapper\MyCoachCategoryUserMapper;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use LeadersLinked\Mapper\JwtTokenMapper;
use LeadersLinked\Authentication\AuthUserIdAdapter;
use LeadersLinked\Model\JwtToken;
use LeadersLinked\Mapper\MicrolearningTopicUserMapper;
use Laminas\Http\Response;
class Module
{
/**
*
* @var Acl
*/
private $acl;
/**
*
* @var AdapterInterface
*/
private $adapter;
/**
*
* @var CurrentUserPlugin
*/
private $currentUserPlugin;
/**
*
* @var CurrentNetworkPlugin
*/
private $currentNetworkPlugin;
/**
*
* @var array
*/
private $routesAuthorized = [];
/**
*
* @var boolean
*/
private $authByOTP = false;
/**
*
* @var boolean
*/
private $authByJWT = false;
/**
*
* @var int
*/
private $jwtID = 0;
/**
*
* @var JwtToken
*/
private $jwtToken;
/**
*
* @var array
*/
private $config;
public function init(ModuleManager $moduleManager)
{
$events = $moduleManager->getEventManager();
$events->attach(ModuleEvent::EVENT_MERGE_CONFIG, array(
$this,
'onMergeConfig'
));
}
public function onMergeConfig(ModuleEvent $event)
{
$configListener = $event->getConfigListener();
$this->config = $configListener->getMergedConfig(false);
$reader = new Ini();
$data = $reader->fromFile('config/leaderslinked.ini');
$prefix = 'leaderslinked';
foreach ($data as $section => $pairs) {
foreach ($pairs as $key => $value) {
$this->config[$prefix . '.' . $section . '.' . $key] = $value;
}
}
$configListener->setMergedConfig($this->config);
}
public function getConfig(): array
{
return include __DIR__ . '/../config/module.config.php';
}
public function onBootstrap(MvcEvent $event)
{
$timezone = $this->config['leaderslinked.runmode.timezone'];
date_default_timezone_set($timezone);
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: *');
header('Access-Control-Allow-Methods: POST, GET, HEAD, OPTIONS');
header('Access-Control-Max-Age: 86400');
http_response_code(204);
exit();
}
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: *');
header('Access-Control-Allow-Method: POST, GET, HEAD, OPTIONS');
header('Access-Control-Max-Age: 86400');
$response = $event->getResponse();
Functions::addCrossSiteToResponse($response);
$event->setResponse($response);
$serviceManager = $event->getApplication()->getServiceManager();
$eventManager = $event->getApplication()->getEventManager();
$eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, [
$this,
'onDispatchError'
], 0);
$eventManager->attach(MvcEvent::EVENT_RENDER_ERROR, [
$this,
'onRenderError'
], 0);
$adapter = $serviceManager->get('leaders-linked-db');
/*
* $session = $serviceManager->get('leaders-linked-session');
* $session->start();
* $session->regenerateId(true);
*/
$translator = $serviceManager->get('MvcTranslator');
$translator->addTranslationFile('phpArray', __DIR__ . '/i18n/validate.php', 'default');
$translator->addTranslationFile('phpArray', __DIR__ . '/i18n/spanish.php', 'default');
\Laminas\Validator\AbstractValidator::setDefaultTranslator($translator);
$headers = $event->getRequest()->getHeaders();
if ($headers->has('token')) {
$device_uuid = Functions::sanitizeFilterString($headers->get('token')->getFieldValue());
} else {
$device_uuid = '';
}
if ($headers->has('secret')) {
$password = Functions::sanitizeFilterString($headers->get('secret')->getFieldValue());
} else {
$password = '';
}
if ($headers->has('rand')) {
$rand = Functions::sanitizeFilterString($headers->get('rand')->getFieldValue());
} else {
$rand = 0;
}
if ($headers->has('created')) {
$timestamp = Functions::sanitizeFilterString($headers->get('created')->getFieldValue());
} else {
$timestamp = 0;
}
$this->currentNetworkPlugin = CurrentNetworkPlugin::getInstance($adapter);
if (! $this->currentNetworkPlugin->hasNetwork()) {
$this->currentNetworkPlugin->fetchDefaultNetwork();
}
if (! $this->currentNetworkPlugin->hasNetwork()) {
$this->fetchDefaultNetwork();
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => '200 Unauthorized - Private network - not found',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
if ($this->currentNetworkPlugin->getNetwork()->status == Network::STATUS_INACTIVE) {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => '200 Unauthorized - Private network - inactive',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
$this->authByOTP = false;
if ($device_uuid && $password && $rand && $timestamp) {
$this->authByOTP = true;
$tokenAuthAdapter = new AuthTokenAdapter($adapter);
$tokenAuthAdapter->setData($device_uuid, $password, $timestamp, $rand);
$authService = new AuthenticationService();
$result = $authService->authenticate($tokenAuthAdapter);
if ($result->getCode() != \Laminas\Authentication\Result::SUCCESS) {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => $result->getMessages()[0],
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
}
$this->jwtID = 0;
$this->authByJWT = false;
$headers = getallheaders();
if (! empty($headers['authorization']) || ! empty($headers['Authorization'])) {
$token = trim(empty($headers['authorization']) ? $headers['Authorization'] : $headers['authorization']);
if (substr($token, 0, 6) == 'Bearer') {
$token = trim(substr($token, 7));
if (! empty($this->config['leaderslinked.jwt.key'])) {
$key = $this->config['leaderslinked.jwt.key'];
try {
$payload = JWT::decode($token, new Key($key, 'HS256'));
if (empty($payload->iss) || $payload->iss != $_SERVER['HTTP_HOST']) {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => 'Unauthorized - JWT - Wrong server',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
$uuid = empty($payload->uuid) ? '' : $payload->uuid;
if ($uuid) {
$jwtTokenMapper = JwtTokenMapper::getInstance($adapter);
$jwtToken = $jwtTokenMapper->fetchOneByUuid($uuid);
if ($jwtToken) {
$this->jwtID = $jwtToken->id;
$_SESSION['aes'] = $jwtToken->aes;
if ($jwtToken->user_id) {
$authByUserId = new AuthUserIdAdapter($adapter);
$authByUserId->setData($jwtToken->user_id);
$authService = new AuthenticationService();
$result = $authService->authenticate($authByUserId);
if ($result->getCode() != \Laminas\Authentication\Result::SUCCESS) {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => $result->getMessages()[0],
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
}
}
else {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => 'Unauthorized - JWT - Expired',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
}
} catch (\Exception $e) {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => 'Unauthorized - JWT - Wrong key',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
}
}
}
if (empty($_SERVER['REDIRECT_URL'])) {
if (empty($_SERVER['REQUEST_URI'])) {
$routeName = '';
} else {
$routeName = $_SERVER['REQUEST_URI'];
}
} else {
$routeName = $_SERVER['REDIRECT_URL'];
}
$routeName = strtolower(trim($routeName));
if (strlen($routeName) > 0 && substr($routeName, 0, 1) == '/') {
$routeName = substr($routeName, 1);
}
$this->currentUserPlugin = CurrentUserPlugin::getInstance($adapter);
if ($this->currentUserPlugin->hasIdentity()) {
if (User::STATUS_BANNED == $this->currentUserPlugin->getUser()->status) {
$code = 200;
$content = json_encode([
'success' => false,
'data' => '403 Forbidden - Banned',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
}
if ($this->authByOTP && substr($routeName, 0, 8) == 'services') {
$checkUserForNetwork = false;
} else {
if ($this->currentUserPlugin->hasIdentity()) {
$checkUserForNetwork = true;
} else {
$checkUserForNetwork = false;
}
}
if ($checkUserForNetwork) {
if (! $routeName || in_array($routeName, [
'signout',
'signin',
'home'
])) {
$checkUserForNetwork = false;
}
}
if ($checkUserForNetwork) {
if ($this->currentUserPlugin->getUser()->network_id != $this->currentNetworkPlugin->getNetworkId()) {
$response = $event->getResponse();
$content = json_encode([
'success' => false,
'data' => '200 Unauthorized - The user is not part of this private network',
'fatal' => true
]);
$this->sendResponse($response, 200, $content);
exit();
}
}
$this->initAcl($event);
$sharedManager = $eventManager->getSharedManager();
$sharedManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, [
$this,
'authPreDispatch'
], 100);
$sharedManager->attach(__NAMESPACE__, MvcEvent::EVENT_DISPATCH, [
$this,
'authPosDispatch'
], - 100);
}
public function initAcl(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
$adapter = $serviceManager->get('leaders-linked-db');
require_once (dirname(__DIR__) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'acl.config.php');
$this->acl = new Acl();
$resources = getAclResources();
foreach ($resources as $resourceName) {
$this->acl->addResource(new GenericResource($resourceName));
}
$usertypes = getAclUsertype();
foreach ($usertypes as $usertype => $resources) {
$this->acl->addRole(new GenericRole($usertype));
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($this->currentUserPlugin->hasIdentity()) {
$user_id = $this->currentUserPlugin->getUserId();
if ($this->currentUserPlugin->getUser()->is_super_user == User::IS_SUPER_USER_YES) {
$resources = getAclSuperAdmin();
foreach ($resources as $resourceName) {
$this->acl->allow(UserType::ADMIN, $resourceName);
}
}
} else {
$user_id = 0;
}
$allowMyCoach = false;
$allowKnowledgeArea = false;
$allowDailyPulse = false;
if ($user_id) {
$allowMicrolearning = $this->isMicroLeargningAccessGranted($adapter, $user_id);
$allowHabit = $this->isHabitsAccessGranted($adapter, $user_id);
} else {
$allowMicrolearning = false;
$allowHabit = false;
}
$companyMapper = CompanyMapper::getInstance($adapter);
$company = $companyMapper->fetchDefaultForNetworkByNetworkId($this->currentNetworkPlugin->getNetwork()->id);
if ($company) {
$companyServiceMapper = CompanyServiceMapper::getInstance($adapter);
$companyService = $companyServiceMapper->fetchOneActiveByCompanyIdAndServiceId($company->id, Service::DAILY_PULSE);
$companyUserMapper = CompanyUserMapper::getInstance($adapter);
$companyUser = $companyUserMapper->fetchOneAcceptedByCompanyIdAndUserId($company->id, $this->currentUserPlugin->getUserId());
if ($companyService) {
$dailyPulseMapper = DailyPulseMapper::getInstance($adapter);
$dailyPulse = $dailyPulseMapper->fetchOneByCompanyId($company->id);
if ($dailyPulse) {
$privacy = $dailyPulse->privacy;
} else {
$privacy = DailyPulse::PRIVACY_COMPANY;
}
if ($privacy == DailyPulse::PRIVACY_PUBLIC) {
$allowDailyPulse = true;
} else {
$allowDailyPulse = ! empty($companyUser);
}
}
$job_description_ids = [];
$organizationPositionMapper = OrganizationPositionMapper::getInstance($adapter);
$records = $organizationPositionMapper->fetchAllByCompanyIdAndEmployeeId($company->id, $this->currentUserPlugin->getUserId());
foreach ($records as $record) {
array_push($job_description_ids, $record->job_description_id);
}
$companyService = $companyServiceMapper->fetchOneActiveByCompanyIdAndServiceId($company->id, Service::KNOWLEDGE_AREA);
if ($companyService) {
if ($job_description_ids) {
$knowledgeAreaCategoryJobDescriptionMapper = KnowledgeAreaCategoryJobDescriptionMapper::getInstance($adapter);
$records = $knowledgeAreaCategoryJobDescriptionMapper->fetchAllByCompanyIdAndJobDescriptionIds($company->id, $job_description_ids);
if (! empty($records)) {
$allowKnowledgeArea = true;
}
}
if ($companyUser && ! $allowKnowledgeArea) {
$knowledgeAreaCategoryUserMapper = KnowledgeAreaCategoryUserMapper::getInstance($adapter);
$records = $knowledgeAreaCategoryUserMapper->fetchAllByUserId($companyUser->user_id);
if (! empty($records)) {
$allowKnowledgeArea = true;
}
}
}
$companyService = $companyServiceMapper->fetchOneActiveByCompanyIdAndServiceId($company->id, Service::MY_COACH);
if ($companyService) {
if ($job_description_ids) {
$myCoachCategoryJobDescriptionMapper = MyCoachCategoryJobDescriptionMapper::getInstance($adapter);
$records = $myCoachCategoryJobDescriptionMapper->fetchAllByCompanyIdAndJobDescriptionIds($company->id, $job_description_ids);
if (! empty($records)) {
$allowKnowledgeArea = true;
}
}
if ($companyUser && ! $allowMyCoach) {
$myCoachCategoryUserMapper = MyCoachCategoryUserMapper::getInstance($adapter);
$records = $myCoachCategoryUserMapper->fetchAllByUserId($companyUser->user_id);
if (! empty($records)) {
$allowMyCoach = true;
}
}
}
} else {
$companyUser = '';
}
$usertype = $this->currentUserPlugin->getUserTypeId();
if ($allowDailyPulse) {
$resources = getAclDailyPulse();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($allowMicrolearning) {
$resources = getAclMicrolearning();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($allowHabit) {
$resources = getAclHabits();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($allowKnowledgeArea) {
$resources = getAclKnowledgeArea();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($allowMyCoach) {
$resources = getAclMyCoach();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($this->currentNetworkPlugin->getNetwork()->default == Network::DEFAULT_YES) {
$usertypes = getAclUsertypeDefaultNetwork();
foreach ($usertypes as $usertype => $resources) {
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
} else {
if ($this->currentUserPlugin->hasIdentity()) {
if ($company) {
if ($companyUser) {
$usertype = $this->currentUserPlugin->getUserTypeId();
if ($companyUser->creator == CompanyUser::CREATOR_YES) {
$resources = getAclUsertypeOtherNetworkCreator();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
if ($companyUser->creator == CompanyUser::CREATOR_NO) {
$resources = getAclUsertypeOtherNetworkNonCreator();
foreach ($resources as $resourceName) {
$this->acl->allow($usertype, $resourceName);
}
}
}
}
}
}
$event->getViewModel()->setVariable('acl', $this->acl);
}
public function onDispatchError(MvcEvent $event)
{
$this->processError($event);
}
public function onRenderError(MvcEvent $event)
{
$this->processError($event);
}
/**
*
* @param \Laminas\Http\Response $response
* @param int $code
* @param string $content
*/
public function sendResponse($response, $code, $content)
{
$headers = $response->getHeaders();
$headers->clearHeaders();
$headers->addHeaderLine('Content-type', 'application/json; charset=UTF-8');
Functions::addCrossSiteToResponse($response);
$response->setStatusCode($code);
$response->setContent($content); // json_encode($data));
$response->send();
exit();
}
public function processError(MvcEvent $event)
{
$error = $event->getError();
if (! $error) {
return;
}
$response = $event->getResponse();
if ('error-exception' == $error) {
$exception = $event->getParam('exception');
error_log($exception->getCode() . ' ' . $exception->getMessage());
error_log($exception->getTraceAsString());
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => $exception->getCode() . ' ' . $exception->getMessage(),
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
} else if ('error-router-no-match' == $error) {
$response = $event->getResponse();
$code = 404;
$content = json_encode([
'success' => false,
'data' => 'error-router-no-match',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
} else if (' error-controller-not-found' == $error) {
$response = $event->getResponse();
$code = 404;
$content = json_encode([
'success' => false,
'data' => 'error-controller-not-found',
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
} else {
$response = $event->getResponse();
$code = 200;
$content = json_encode([
'success' => false,
'data' => $error,
'fatal' => true
]);
$this->sendResponse($response, $code, $content);
}
exit();
}
public function authPreDispatch(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
$adapter = $serviceManager->get('leaders-linked-db');
$routeName = $event->getRouteMatch()->getMatchedRouteName();
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? trim(strtoupper($_SERVER['REQUEST_METHOD'])) : '';
if ($requestMethod == 'POST') {
if ($this->authByOTP && substr($routeName, 0, 8) == 'services') {
$exclude = true;
} else {
$exclude = false;
$usertypes = getAclUsertype();
foreach ($usertypes[UserType::GUEST] as $resourceName) {
if ($routeName == $resourceName) {
$exclude = true;
break;
}
}
}
if (! $exclude) {
$httpToken = isset($_SERVER['HTTP_X_CSRF_TOKEN']) ? $_SERVER['HTTP_X_CSRF_TOKEN'] : '';
if ($this->jwtID) {
$jwtTokenMapper = JwtTokenMapper::getInstance($this->adapter);
$jwtToken = $jwtTokenMapper->fetchOne($this->jwtID);
if ($jwtToken) {
$sessionToken = $jwtToken->csrf;
// $jwtToken->csrf= '';
// $jwtTokenMapper->update($jwtToken);
} else {
$sessionToken = '';
}
} else {
$sessionToken = '';
}
// error_log('$this->jwtID = ' . $this->jwtID . ' $httpToken = ' . $httpToken . ' $sessionToken = ' . $sessionToken);
// if ( $httpToken != $sessionToken) {
// $response = $event->getResponse();
// $this->sendResponse($response, 200, json_encode(['success' => false, 'data' => 'Unauthorized - CSRF fail', 'fatal' => true]));
// }
}
}
if ($this->currentUserPlugin->hasIdentity()) {
$user = $this->currentUserPlugin->getUser();
$user_id = $user->id;
$userTypeId = $user->usertype_id;
} else {
$userTypeId = UserType::GUEST;
$user_id = 0;
}
if ($this->acl->isAllowed($userTypeId, $routeName)) {
$user = $this->currentUserPlugin->getUser();
if ($user) {
$updateLastActivity = true;
if ('chat' == substr($routeName, 0, 4)) {
$updateLastActivity = false;
}
if ('inmail' == substr($routeName, 0, 6)) {
$updateLastActivity = false;
}
if ('check-session' == $routeName) {
$updateLastActivity = false;
}
if ($updateLastActivity) {
$userMapper = UserMapper::getInstance($adapter);
$userMapper->updateLastActivity($user->id);
}
}
} else {
$response = $event->getResponse();
$response->setStatusCode(200);
$response->setContent(json_encode([
'success' => false,
'data' => 'Unauthorized - Does not have permission',
'fatal' => true
]));
$response->send();
exit();
}
}
public function authPosDispatch(MvcEvent $event)
{
// $response = $event->getResponse();
// Functions::addCrossSiteToResponse($response);
}
/**
*
* @param \Laminas\Db\Adapter\AdapterInterface $adapter
* @param int $user_id
* @return boolean
*/
private function isMicroLeargningAccessGranted($adapter, $user_id)
{
$accessGranted = false;
$topicUserMapper = \LeadersLinked\Mapper\MicrolearningTopicUserMapper::getInstance($adapter);
$now = $topicUserMapper->getDatebaseNow();
$records = $topicUserMapper->fetchAllActiveByUserId($user_id);
foreach ($records as $record) {
if ($record->access != \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_UNLIMITED && $record->access != \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_PAY_PERIOD) {
continue;
}
if ($record->access == \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_PAY_PERIOD) {
if ($now < $record->paid_from || $now > $record->paid_to) {
continue;
}
}
$accessGranted = true;
break;
}
return $accessGranted;
}
/**
*
* @param \Laminas\Db\Adapter\AdapterInterface $adapter
* @param int $user_id
* @return boolean
*/
private function isHabitsAccessGranted($adapter, $user_id)
{
$accessGranted = false;
$habitUserMapper = \LeadersLinked\Mapper\HabitUserMapper::getInstance($adapter);
$now = $habitUserMapper->getDatebaseNow();
$records = $habitUserMapper->fetchAllActiveByUserId($user_id);
foreach ($records as $record) {
if ($record->access != \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_UNLIMITED && $record->access != \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_PAY_PERIOD) {
continue;
}
if ($record->access == \LeadersLinked\Model\MicrolearningTopicUser::ACCESS_PAY_PERIOD) {
if ($now < $record->paid_from || $now > $record->paid_to) {
continue;
}
}
$accessGranted = true;
break;
}
return $accessGranted;
}
}