<?php

namespace Kanboard\Api\Middleware;

use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\AuthenticationFailureException;
use JsonRPC\MiddlewareInterface;
use Kanboard\Core\Base;

/**
 * Class AuthenticationApiMiddleware
 *
 * @package Kanboard\Api\Middleware
 * @author  Frederic Guillot
 */
class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
{
    private $user_allowed_procedures = array(
        'getMe',
        'getMyDashboard',
        'getMyActivityStream',
        'createMyPrivateProject',
        'getMyProjectsList',
        'getMyProjects',
        'getMyOverdueTasks',
    );

    private $both_allowed_procedures = array(
        'getTimezone',
        'getVersion',
        'getDefaultTaskColor',
        'getDefaultTaskColors',
        'getColorList',
        'getProjectById',
        'getTask',
        'getTaskByReference',
        'getAllTasks',
        'openTask',
        'closeTask',
        'moveTaskPosition',
        'createTask',
        'updateTask',
        'getBoard',
        'getProjectActivity',
        'getOverdueTasksByProject',
        'searchTasks',
    );

    /**
     * Execute Middleware
     *
     * @access public
     * @param  string $username
     * @param  string $password
     * @param  string $procedureName
     * @throws AccessDeniedException
     * @throws AuthenticationFailureException
     */
    public function execute($username, $password, $procedureName)
    {
        $this->dispatcher->dispatch('app.bootstrap');

        if ($this->isUserAuthenticated($username, $password)) {
            $this->checkProcedurePermission(true, $procedureName);
            $this->userSession->initialize($this->userModel->getByUsername($username));
        } elseif ($this->isAppAuthenticated($username, $password)) {
            $this->checkProcedurePermission(false, $procedureName);
        } else {
            $this->logger->error('API authentication failure for '.$username);
            throw new AuthenticationFailureException('Wrong credentials');
        }
    }

    /**
     * Check user credentials
     *
     * @access public
     * @param  string  $username
     * @param  string  $password
     * @return boolean
     */
    private function isUserAuthenticated($username, $password)
    {
        return $username !== 'jsonrpc' &&
        ! $this->userLockingModel->isLocked($username) &&
        $this->authenticationManager->passwordAuthentication($username, $password);
    }

    /**
     * Check administrative credentials
     *
     * @access public
     * @param  string  $username
     * @param  string  $password
     * @return boolean
     */
    private function isAppAuthenticated($username, $password)
    {
        return $username === 'jsonrpc' && $password === $this->getApiToken();
    }

    /**
     * Get API Token
     *
     * @access private
     * @return string
     */
    private function getApiToken()
    {
        if (defined('API_AUTHENTICATION_TOKEN')) {
            return API_AUTHENTICATION_TOKEN;
        }

        return $this->configModel->get('api_token');
    }

    public function checkProcedurePermission($is_user, $procedure)
    {
        $is_both_procedure = in_array($procedure, $this->both_allowed_procedures);
        $is_user_procedure = in_array($procedure, $this->user_allowed_procedures);

        if ($is_user && ! $is_both_procedure && ! $is_user_procedure) {
            throw new AccessDeniedException('Permission denied');
        } elseif (! $is_user && ! $is_both_procedure && $is_user_procedure) {
            throw new AccessDeniedException('Permission denied');
        }

        $this->logger->debug('API call: '.$procedure);
    }
}
