tao-test/app/taoLti/controller/ConsumerAdmin.php

270 lines
8.4 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2019 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
*/
namespace oat\taoLti\controller;
use common_exception_BadRequest as BadRequestException;
use core_kernel_classes_Class as KernelClass;
use core_kernel_classes_Resource as KernelResource;
use oat\tao\model\controller\SignedFormInstance;
use oat\tao\model\oauth\DataStore;
use oat\taoLti\models\classes\ConsumerService;
use oat\taoLti\models\classes\Security\Business\Contract\SecretKeyServiceInterface;
use oat\taoLti\models\classes\Security\Business\Domain\Exception\SecretKeyGenerationException;
use tao_actions_form_CreateInstance as CreateInstanceContainer;
use tao_actions_SaSModule;
use tao_helpers_form_Form as Form;
use tao_helpers_form_FormFactory as FormFactory;
use tao_helpers_Uri as UriHelper;
use tao_models_classes_dataBinding_GenerisFormDataBinder as FormDataBinder;
use tao_models_classes_dataBinding_GenerisFormDataBindingException as FormDataBindingException;
/**
* This controller allows the additon and deletion
* of LTI Oauth Consumers
*
* @package taoLti
* @license GPLv2 http://www.opensource.org/licenses/gpl-2.0.php
*/
class ConsumerAdmin extends tao_actions_SaSModule
{
private const EXCLUDED_FIELDS = [
'id',
DataStore::PROPERTY_OAUTH_SECRET,
DataStore::PROPERTY_OAUTH_CALLBACK,
];
private const SECRET_REGENERATION_KEY = 'regenerate_secret';
/** @var KernelClass|null */
private $currentClass;
/** @var KernelResource|null */
private $currentInstance;
/**
* @inheritDoc
*
* @throws SecretKeyGenerationException
*/
public function addInstanceForm(): void
{
if (!$this->isXmlHttpRequest()) {
throw new BadRequestException('wrong request mode');
}
$form = $this->createNewInstanceForm();
$this->handleNewInstanceSubmission($form);
$this->setData('formTitle', __('Create instance of ') . $this->getCurrentClass()->getLabel());
$this->setData('myForm', $form->render());
$this->setView('form.tpl', 'tao');
}
/**
* @inheritDoc
*
* @throws FormDataBindingException
*/
public function editInstance(): void
{
$form = $this->createExistingInstanceForm();
$this->handleExistingInstanceSubmission($form);
$this->setData('formTitle', __('Edit Instance'));
$this->setData('myForm', $form->render());
$this->setView('form.tpl', 'tao');
}
/**
* @inheritDoc
*/
protected function getClassService()
{
return $this->getServiceLocator()->get(ConsumerService::class);
}
/**
* @inheritDoc
*/
protected function getCurrentClass(): KernelClass
{
if (null === $this->currentClass) {
$this->currentClass = parent::getCurrentClass();
}
return $this->currentClass;
}
/**
* @noinspection PhpDocMissingThrowsInspection
*
* @param string $parameterName
*
* @return KernelResource
*/
protected function getCurrentInstance($parameterName = 'uri'): KernelResource
{
if (null === $this->currentInstance) {
/** @noinspection PhpUnhandledExceptionInspection */
$this->currentInstance = parent::getCurrentInstance($parameterName);
}
return $this->currentInstance;
}
private function createNewInstanceForm(): Form
{
$formContainer = new CreateInstanceContainer(
[$this->getCurrentClass()],
[
CreateInstanceContainer::CSRF_PROTECTION_OPTION => true,
'excludedProperties' => self::EXCLUDED_FIELDS,
]
);
return $formContainer->getForm();
}
private function createExistingInstanceForm(): Form
{
$myFormContainer = new SignedFormInstance(
$this->getCurrentClass(),
$this->getCurrentInstance(),
[SignedFormInstance::CSRF_PROTECTION_OPTION => true]
);
$form = $myFormContainer->getForm();
$this->enrichWithSecretField($form);
$this->enrichWithRegenerateSecretAction($form);
$this->enrichWithRegenerateSecretDisclaimer($form);
return $form;
}
private function enrichWithSecretField(Form $form): void
{
$secret = $form->getElement(UriHelper::encode(DataStore::PROPERTY_OAUTH_SECRET));
$secret->addAttribute('readonly', 'readonly');
$secret->addClass('copy-to-clipboard');
$secret->addAttribute(
'data-copy-success-feedback',
__('Oauth consumer secret has been copied to the clipboard')
);
foreach (self::EXCLUDED_FIELDS as $excludedField) {
$form->removeElement(UriHelper::encode($excludedField));
}
$form->addElement($secret);
}
private function enrichWithRegenerateSecretAction(Form $form): void
{
/** @noinspection PhpUnhandledExceptionInspection */
$regenerateButton = FormFactory::getElement(self::SECRET_REGENERATION_KEY, 'Button');
$regenerateButton->addClass('small form-submitter');
$regenerateButton->addAttribute('style', 'width: 100%;');
$regenerateButton->setValue(__('Generate a new secret key'));
$form->addElement($regenerateButton);
}
private function enrichWithRegenerateSecretDisclaimer(Form $form): void
{
/** @noinspection PhpUnhandledExceptionInspection */
$regenerateDisclaimer = FormFactory::getElement('regenerate_disclaimer', 'Free');
$regenerateDisclaimer->setValue(
__('Please consider that generating a new key will replace the previously generated one.')
);
$form->addElement($regenerateDisclaimer);
}
/**
* @param Form $form
*
* @throws SecretKeyGenerationException
*/
private function handleNewInstanceSubmission(Form $form): void
{
if ($form->isSubmited() && $form->isValid()) {
$values = $this->extractValues($form);
$values[DataStore::PROPERTY_OAUTH_SECRET] = $this->getSecretKeyService()->generate();
$instance = $this->createInstance([$this->getCurrentClass()], $values);
$this->setData('message', __('%s created', $instance->getLabel()));
$this->setData('reload', true);
}
}
/**
* @param Form $form
*
* @throws FormDataBindingException
*/
private function handleExistingInstanceSubmission(Form $form): void
{
if ($form->isSubmited() && $form->isValid()) {
$values = $this->extractValues($form);
$requestBody = $this->getPsrRequest()->getParsedBody();
if (!empty($requestBody[self::SECRET_REGENERATION_KEY])) {
$values[DataStore::PROPERTY_OAUTH_SECRET] = $this->getSecretKeyService()->generate();
}
$this->updateCurrentInstance($values);
$this->setData('message', __('Instance saved'));
$this->setData('reload', true);
}
}
private function extractValues(Form $form): array
{
$values = $form->getValues();
$values = array_diff_key($values, array_flip(self::EXCLUDED_FIELDS));
return $values;
}
/**
* @param array $values
*
* @throws FormDataBindingException
*/
private function updateCurrentInstance(array $values): void
{
(new FormDataBinder($this->getCurrentInstance()))->bind($values);
}
private function getSecretKeyService(): SecretKeyServiceInterface
{
/** @noinspection PhpIncompatibleReturnTypeInspection */
return $this->getServiceLocator()->get(SecretKeyServiceInterface::class);
}
}