Skip to content
Snippets Groups Projects
UserController.php 16.7 KiB
Newer Older
<?php
/**
 * Copyright (C) 2019 Leipzig University Library
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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.
 *
 * @author   Gregor Gawol <gawol@ub.uni-leipzig.de>
 * @author   Sebastian Kehr <kehr@ub.uni-leipzig.de>
 * @license  http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
 */

namespace fid\Controller;

use fid\FormModel\PasswordChangeModel;
use fid\FormModel\PasswordResetModel;
use fid\FormModel\UserCreateModel;
use fid\FormModel\UserInitModel;
use fid\FormModel\UserUpdateModel;
use fid\Service\Client;
use fid\Service\ClientException;
use fid\Service\DataTransferObject\Library;
use fid\Service\DataTransferObject\User;
use Symfony\Component\Serializer\SerializerAwareTrait;
use Symfony\Component\Serializer\SerializerInterface;
use VuFind\Auth\Manager as Authenticator;
use VuFind\Controller\AbstractBase;
use VuFind\Exception\Auth as AuthException;
use Zend\Form\Annotation\AnnotationBuilder;
use Zend\Form\Form;
use Zend\Http\PhpEnvironment\Request;
use Zend\Http\Response;
use Zend\Mvc\Plugin\FlashMessenger\FlashMessenger;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\View\Model\ViewModel;

class UserController extends AbstractBase
{
    use SerializerAwareTrait;

    /**
     * @var Authenticator
     */
    protected $authenticator;

    /**
     * @var SerializerInterface
     */
    protected $serializer;

    /**
     * @var AnnotationBuilder
     */
    protected $builder;

    /**
     * @var Client
     */
    protected $client;

    /**
     * @var array
     */
    protected $config;

    /**
     * RegistrationController constructor.
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @param SerializerInterface     $serializer
     * @param Authenticator           $authenticator
     * @param AnnotationBuilder       $builder
     * @param Client                  $client
     * @param array                   $config
     */
    public function __construct(
        ServiceLocatorInterface $serviceLocator,
        SerializerInterface $serializer,
        Authenticator $authenticator,
        AnnotationBuilder $builder,
        Client $client,
        array $config
    ) {
        parent::__construct($serviceLocator);
        $this->authenticator = $authenticator;
        $this->serializer = $serializer;
        $this->builder = $builder;
        $this->client = $client;
        $this->config = $config;
    }

    /**
     * @return ViewModel
     */
    public function initAction()
    {
        /** @var Request $request */
        $request = $this->getRequest();
        $form = $this->builder->createForm(UserInitModel::class);
        $forwarded = $this->params()->fromRoute('forwarded', false);

        if ($submitted = $this->formWasSubmitted()) {
            $form->setData($request->getPost());
            if (!$forwarded && $form->isValid()) {
                return $this->init($form);
            }
        }

        $view = $this->createViewModel();
        $view->setVariables(compact('form'));
        $view->setTemplate('fid/user/init');

        return $view;
    }

    /**
     * @return Response|ViewModel
     */
    public function createAction()
    {
        /** @var Request $request */
        $request = $this->getRequest();
        $query = $request->getQuery();
        $messenger = $this->getMessenger();

        if ($credentials = $query->get('logon')) {
            try {
                $this->client->logon($credentials);
            } catch (ClientException $exception) {
                $message = $exception->getCode() === 401
                    ? 'fid::user_create_error_expired'
                    : 'fid::user_create_error';
                $messenger->addErrorMessage($this->translate($message));
                return $this->redirect()->toRoute('fid/user/init');
            }

            $query->offsetUnset('logon');
            return $this->redirect()->toRoute('fid/user/create', [], [
                'query' => $query->toArray()
            ]);
        }

        $form = $this->builder->createForm(UserCreateModel::class);

        if ($this->formWasSubmitted()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                return $this->create($form);
            }
        } else {
            $form->setData($query);
        }

        try {
            $libraries = array_map(function (Library $libary) {
                return $libary->getLabel();
            }, $this->client->requestLibraryList());
        } catch (ClientException $exception) {
            $message = 'fid::user_create_error';
            $messenger->addErrorMessage($this->translate($message));
            return $this->redirect()->toRoute('fid/user/init');
        }

        $view = $this->createViewModel();
        $accessLevels = $this->config['Security']['access_levels'];
        $view->setVariables(compact('form', 'accessLevels', 'libraries'));
        $view->setTemplate('fid/user/create');

        return $view;
    }

    public function updateAction()
    {
        /** @var Request $request */
        $request = $this->getRequest();
        $form = $this->builder->createForm(UserUpdateModel::class);

        if ($this->formWasSubmitted()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                return $this->update($form);
            }
        }

        try {
            $user = $this->client->requestUserDetails();
            $libraries = array_map(function (Library $libary) {
                return $libary->getLabel();
            }, $this->client->requestLibraryList());
        } catch (ClientException $exception) {
            $this->setFollowupUrlToReferer();
            $message = $exception->getCode() === 401
                ? 'fid::user_update_error_expired'
                : 'fid::user_update_error';
            $this->getMessenger()->addErrorMessage($this->translate($message));
            return $this->redirect()->toRoute('myresearch-home');
        }

        $viewModel = $this->createViewModel();
        $viewModel->setVariables(compact('form', 'user', 'libraries'));
        $viewModel->setTemplate('fid/user/update');

        return $viewModel;
    }

    public function policyAction()
    {
        $viewModel = $this->createViewModel();
        $viewModel->setTemplate('fid/user/policy');

        return $viewModel;
    }

    public function termsAction()
    {
        $viewModel = $this->createViewModel();
        $viewModel->setTemplate('fid/user/terms');

        return $viewModel;
    }

    /**
     * Reset password action - Allows the reset password form to appear.
     *
     * @return ViewModel
     */
    public function resetPasswordAction()
    {
        /** @var Request $request */
        $request = $this->getRequest();
        $form = $this->builder->createForm(PasswordResetModel::class);
        $forwarded = $this->params()->fromRoute('forwarded', false);

        if ($submitted = $this->formWasSubmitted()) {
            $form->setData($request->getPost());
            if (!$forwarded && $form->isValid()) {
                return $this->sendResetPassword($form);
            }
        }

        $view = $this->createViewModel();
        $view->setVariables(compact('form'));
        $view->setTemplate('fid/user/password-reset');

        return $view;
    }

    /**
     * Reset password action - Allows the change password form to appear.
     *
     * @return Response|ViewModel
     */
    public function changePasswordAction()
    {
        /** @var Request $request */
        $request = $this->getRequest();
        $query = $request->getQuery();
        $messenger = $this->getMessenger();

        if ($credentials = $query->get('logon')) {
            try {
                $this->client->logon($credentials);
            } catch (ClientException $exception) {
                $message = $exception->getCode() === 401
                    ? 'fid::password_change_error_expired'
                    : 'fid::password_change_error';
                $messenger->addErrorMessage($this->translate($message));
                return $this->redirect()->toRoute('fid/user/reset-password');
            }

            $query->offsetUnset('logon');
            return $this->redirect()->toRoute('fid/user/change-password', [], [
                'query' => $query->toArray()
            ]);
        }

        $form = $this->builder->createForm(PasswordChangeModel::class);

        if ($this->formWasSubmitted()) {
            $form->setData($request->getPost());
            if ($form->isValid()) {
                return $this->changePassword($form);
            }
        } else {
            $form->setData($query);
        }

        $view = $this->createViewModel();
        $view->setVariables(compact('form'));
        $view->setTemplate('fid/user/password-change');

        return $view;
    }

    /**
     * @param Form $form
     *
     * @return mixed|Response
     */
    protected function init(Form $form)
    {
        /** @var UserInitModel $model */
        $messenger = $this->getMessenger();
        $model = $form->getHydrator()
            ->hydrate($form->getData(), new UserInitModel());

        /** @noinspection PhpUndefinedFieldInspection */
        $query['lng'] = $this->layout()->userLang;
        $username = $query['username'] = $model->getUsername();
        $firstname = $query['firstname'] = $model->getFirstname();
        $lastname = $query['lastname'] = $model->getLastname();
        $baseUrl = $this->url()->fromRoute('fid/user/create',
            [], ['query' => $query, 'force_canonical' => true]);

        try {
            $this->client->requestRegistrationLink($baseUrl, $username,
                $firstname, $lastname);
        } catch (ClientException $exception) {
            $message = $exception->getCode() === 400
                ? 'fid::user_init_error_username'
                : 'fid::user_init_error';
            $messenger->addErrorMessage($this->translate($message));
            return $this->forward()->dispatch(self::class, [
                'action'    => 'init',
                'forwarded' => true
            ]);
        }

        $message = $this->translate('fid::user_init_success');
        $messenger->addSuccessMessage(sprintf($message, $username));
        return $this->redirect()->toRoute('myresearch-home');
    }

    protected function create(Form $form)
    {
        $messenger = $this->getMessenger();
        /** @var UserCreateModel $model */
        $model = $form->getHydrator()
            ->hydrate($form->getData(), new UserCreateModel());

        $user = new User();
        $user->setUsername($username = $model->getUsername());
        $user->setPassword($password = $model->getPassword());
        $user->setHomeLibrary($model->getHomeLibrary());
        $user->setPermissions([$model->getAccessLevel() => 'requested']);
        $user->setSalutation($model->getSalutation());
        $user->setAcademicTitle($model->getAcademicTitle());
        $user->setFirstname($model->getFirstname());
        $user->setLastname($model->getLastname());
        $user->setYearOfBirth($model->getYearOfBirth());
        $user->setCollege($model->getCollege());
        $user->setJobTitle($model->getJobTitle());

        try {
            $this->client->requestUserCreation($user);
            $message = $this->translate('fid::user_create_success');
            $messenger->addSuccessMessage($message);
            /** @noinspection PhpParamsInspection */
            $this->authenticator->create($this->getRequest());
        } catch (ClientException $exception) {
            $message = $this->translate('fid::user_create_error');
            $messenger->addErrorMessage($message);
        } catch (AuthException $e) {
            $message = $this->translate('fid::user_create_error_autologon');
            $messenger->addWarningMessage($message);
        }

        return $this->redirect()->toRoute('myresearch-home', [], [
            'query' => ['redirect' => false]
        ]);
    }

    /**
     * @param Form $form
     *
     * @return Response
     */
    protected function update(Form $form)
    {
        $messenger = $this->getMessenger();
        /** @var UserUpdateModel $model */
        $model = $form->getHydrator()->hydrate($form->getData(),
            new UserUpdateModel());

        try {
            $user = $this->client->requestUserDetails();
            $user->setHomeLibrary($model->getHomeLibrary());
            $user->setSalutation($model->getSalutation());
            $user->setAcademicTitle($model->getAcademicTitle());
            $user->setFirstname($model->getFirstname());
            $user->setLastname($model->getLastname());
            $user->setYearOfBirth($model->getYearOfBirth());
            $user->setCollege($model->getCollege());
            $user->setJobTitle($model->getJobTitle());
            $this->client->requestUserUpdate($user);
            $message = $this->translate('fid::user_update_success');
            $messenger->addSuccessMessage($message);
        } catch (ClientException $exception) {
            if (in_array($exception->getCode(), [403])) {
                $message = $this->translate('fid::user_update_error_' . $exception->getCode());
            } else {
                $message = $this->translate('fid::user_update_error');
            }

            $messenger->addErrorMessage($message);
        }

        return $this->redirect()->toRoute('myresearch-home', [], [
            'query' => ['redirect' => false]
        ]);
    }

    protected function sendResetPassword(Form $form)
    {
        /** @var PasswordResetModel $model */
        $messenger = $this->getMessenger();
        $model = $form->getHydrator()->hydrate(
            $form->getData(), new PasswordResetModel());
        $username = $model->getUsername();

        try {
            /** @noinspection PhpUndefinedFieldInspection */
            $query['lng'] = $this->layout()->userLang;
            $baseUrl = $this->url()->fromRoute('fid/user/change-password',
                [], ['query' => $query, 'force_canonical' => true]);
            $this->client->requestPasswordLink($baseUrl, $username);
            $message = $this->translate('fid::password_reset_success');
            $messenger->addSuccessMessage(sprintf($message, $username));
        } catch (ClientException $exception) {
            $message = $exception->getCode() === 400
                ? $this->translate('fid::password_reset_error_username')
                : $this->translate('fid::password_reset_error');
            $messenger->addErrorMessage(sprintf($message, $username));
            return $this->redirect()->toRoute('fid/user/reset-password');
        }

        return $this->redirect()->toRoute('myresearch-home');
    }

    protected function changePassword(Form $form)
    {
        /** @var PasswordChangeModel $model */
        $messenger = $this->getMessenger();
        $model = $form->getHydrator()->hydrate(
            $form->getData(), new PasswordChangeModel());

        try {
            $user = $this->client->requestUserDetails();
            $user->setPassword($password = $model->getPassword());
            $this->client->requestUserPasswordUpdate($user);
            $message = $this->translate('fid::password_change_success');
            $message = sprintf($message, $username = $user->getUsername());
            $messenger->addSuccessMessage($message);
            /** @var Request $request */
            $request = clone $this->getRequest();
            $params = clone $request->getPost();
            $params->set('username', $username);
            $params->set('password', $password);
            $request->setPost($params);
            $this->authenticator->create($request);
        } catch (ClientException $exception) {
            $message = $this->translate('fid::password_change_error');
            $messenger->addErrorMessage($message);
        } catch (AuthException $e) {
            $message = $this->translate('fid::password_change_error_autologon');
            $messenger->addErrorMessage($message);
        }

        return $this->redirect()->toRoute('myresearch-home', [], [
            'query' => ['redirect' => false]
        ]);
    }

    protected function getMessenger(): FlashMessenger
    {
        /** @noinspection PhpUndefinedMethodInspection */
        /** @var FlashMessenger $messenger */
        return $this->flashMessenger();
    }
}