* @author Joel Bout */ class ActionEnforcer implements IExecutable, ServiceManagerAwareInterface, TaoLoggerAwareInterface { use ServiceManagerAwareTrait; use LoggerAwareTrait; private $extension; private $controllerClass; private $action; private $parameters; private $request; private $response; public function __construct($extensionId, $controller, $action, array $parameters) { $this->extension = $extensionId; $this->controllerClass = $controller; $this->action = $action; $this->parameters = $parameters; } protected function getExtensionId() { return $this->extension; } protected function getControllerClass() { return $this->controllerClass; } protected function getAction() { return $this->action; } protected function getParameters() { return $this->parameters; } protected function getController() { $controllerClass = $this->getControllerClass(); if (!class_exists($controllerClass)) { throw new ActionEnforcingException('Controller "' . $controllerClass . '" could not be loaded.', $controllerClass, $this->getAction()); } $controller = $this->getClassInstance($controllerClass); $this->propagate($controller); if ($controller instanceof Controller) { $controller->setRequest($this->getRequest()); $controller->setResponse($this->getResponse()); } if ($controller instanceof CommonModuleInterface) { $controller->initialize(); } return $controller; } private function getClassInstance(string $className): object { $serviceId = defined("$className::SERVICE_ID") ? $className::SERVICE_ID : $className; return $this->getServiceLocator()->has($serviceId) ? $this->getServiceLocator()->get($serviceId) : $this->propagate(new $className); } protected function getRequest() { if (!$this->request) { $this->request = ServerRequest::fromGlobals(); } return $this->request; } protected function getResponse() { if (!$this->response) { $this->response = new Response(); } return $this->response; } /** * @throws PermissionException * @throws \common_exception_Error * @throws \common_exception_MissingParameter * @throws tao_models_classes_AccessDeniedException */ protected function verifyAuthorization() { $user = common_session_SessionManager::getSession()->getUser(); if (!AclProxy::hasAccess($user, $this->getControllerClass(), $this->getAction(), $this->getParameters())) { $func = new FuncProxy(); $data = new DataAccessControl(); //now go into details to see which kind of permissions are not correct if ( $func->hasAccess($user, $this->getControllerClass(), $this->getAction(), $this->getParameters()) && !$data->hasAccess($user, $this->getControllerClass(), $this->getAction(), $this->getParameters()) ) { throw new PermissionException($user->getIdentifier(), $this->getAction(), $this->getControllerClass(), $this->getExtensionId()); } throw new tao_models_classes_AccessDeniedException($user->getIdentifier(), $this->getAction(), $this->getControllerClass(), $this->getExtensionId()); } } public function __invoke(ServerRequestInterface $request, ResponseInterface $response = null) { $this->request = $request; $this->response = $response; $this->execute(); } /** * @throws ActionEnforcingException * @throws ReflectionException * @throws \common_exception_Error * @throws \common_exception_MissingParameter * @throws tao_models_classes_AccessDeniedException */ public function execute() { // Are we authorized to execute this action? try { $this->verifyAuthorization(); } catch (PermissionException $pe) { //forward the action (yes it's an awful hack, but far better than adding a step in Bootstrap's dispatch error). Context::getInstance()->setExtensionName('tao'); $this->action = 'denied'; $this->controllerClass = 'tao_actions_Permission'; $this->extension = 'tao'; } $response = $this->resolve($this->getRequest()); $emitter = new ResponseEmitter(); $emitter($response); } /** * @throws ActionEnforcingException * @throws ReflectionException * @throws \common_exception_Error */ public function resolve(ServerRequestInterface $request): ResponseInterface { $this->request = $request; /** @var ControllerService $controllerService */ $controllerService = $this->getServiceLocator()->get(ControllerService::SERVICE_ID); try { $controllerService->checkController($this->getControllerClass()); $action = $controllerService->getAction($this->getControllerClass(), $this->getAction()); } catch (RouterException $e) { throw new ActionEnforcingException($e->getMessage(), $this->getControllerClass(), $this->getAction()); } $this->response = $this->getMiddlewareRequestHandler()->withOriginalResponse($this->getResponse())->handle( $request ); $controller = $this->getController(); if (!method_exists($controller, $action)) { throw new ActionEnforcingException( "Unable to find the action '" . $action . "' in '" . get_class($controller) . "'.", $this->getControllerClass(), $this->getAction() ); } $actionParameters = $this->resolveParameters($request, $controller, $action); // Action method is invoked, passing request parameters as method parameters. $user = common_session_SessionManager::getSession()->getUser(); $this->logDebug('Invoking ' . get_class($controller) . '::' . $action . ' by ' . $user->getIdentifier(), ['GENERIS', 'CLEARRFW']); $eventManager = $this->getServiceLocator()->get(EventManager::SERVICE_ID); $eventManager->trigger(new BeforeAction()); $response = call_user_func_array([$controller, $action], $actionParameters); return $response instanceof ResponseInterface ? $response : $controller->getPsrResponse(); } /** * @throws ReflectionException */ private function resolveParameters(ServerRequestInterface $request, $controller, string $action): array { // search parameters method $reflect = new ReflectionMethod($controller, $action); $parameters = $this->getParameters(); $actionParameters = []; foreach ($reflect->getParameters() as $param) { $paramName = $param->getName(); $paramType = $param->getType(); $paramTypeName = $paramType !== null ? $paramType->getName() : null; if (isset($parameters[$paramName])) { $actionParameters[$paramName] = $parameters[$paramName]; } elseif($paramTypeName === ServerRequest::class) { $actionParameters[$paramName] = $request; } elseif (class_exists($paramTypeName) || interface_exists($paramTypeName)) { $actionParameters[$paramName] = $this->getClassInstance($paramTypeName); } elseif (!$param->isDefaultValueAvailable()) { $this->logWarning('Missing parameter ' . $paramName . ' for ' . $this->getControllerClass() . '@' . $action); } } return $actionParameters; } private function getMiddlewareRequestHandler(): MiddlewareRequestHandler { return $this->getServiceManager()->get(MiddlewareRequestHandler::SERVICE_ID); } }