<?php

/**
 * Lifestyle Webconsulting GmbH
 *
 * LICENSE: This Software is the property of Lifestyle Webconsulting GmbH (Aschaffenburg, Germany)
 * and is private by copyright law - it is NOT Freeware.
 *
 * Any unauthorized use of this software without a valid license
 * is a violation of the license agreement and will be prosecuted by
 * civil and criminal law.
 *
 * @copyright  2017 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 */

namespace Sso\WebserviceBundle\Tests\Functional\Services\Tests\Version2;

use Sso\WebserviceBundle\Tests\Functional\Services\Helper\Credentials;
use Sso\WebserviceBundle\Tests\Functional\Services\Helper\Version2\FunctionalTestCase;
use Sso\WebserviceBundle\Tests\Functional\Services\Helper\Version2\Model\Application;
use Sso\WebserviceBundle\Tests\Functional\Services\Helper\Version2\Model\User;
use Sso\WebserviceBundle\Tests\Functional\Services\Helper\Configuration;

/**
 * Class SecurityTest
 *
 * Application-Access-Control disabled
 * - Invalid api user, valid token => FULL access
 * - Invalid api user, invalid token => no access
 * - Minimal api user, valid token => FULL access
 * - Minimal api user, invalid token => no access
 * - Granted api user, valid token => FULL access
 * - Granted api user, invalid token => no access
 *
 * Application-Access-Control enabled
 * - Invalid api user, valid token => NO access
 * - Invalid api user, invalid token => no access
 * - Minimal api user, valid token => LIMITED access
 * - Minimal api user, invalid token => no access
 * - Granted api user, valid token => FULL access
 * - Granted api user, invalid token => no access
 *
 * @copyright  2017 Lifestyle Webconsulting GmbH
 * @link       http://www.life-style.de
 * @package Sso\WebserviceBundle\Tests\Functional\Services\Tests\Version2
 */
class SecurityTest extends FunctionalTestCase
{
    /**
     * @return array
     */
    public function getTestScenes()
    {
        return [
            // Application-Access-Control disabled
            ['user-api-no-access',      false, true,  200, 'response-structure-success.xml',  'Testcase: application-access-control disabled, invalid api user, valid token => FULL access'],
            ['user-api-no-access',      false, false, 401, 'response-structure-error.xml',    'Testcase: application-access-control disabled, invalid api user, invalid token => no access'],
            ['user-api-minimal-access', false, true,  200, 'response-structure-success.xml',  'Testcase: application-access-control disabled, minimal api user, valid token => FULL access'],
            ['user-api-minimal-access', false, false, 401, 'response-structure-error.xml',    'Testcase: application-access-control disabled, minimal api user, invalid token => no access'],
            ['user-api-grant-access',   false, true,  200, 'response-structure-success.xml',  'Testcase: application-access-control disabled, granted api user, valid token => FULL access'],
            ['user-api-grant-access',   false, false, 401, 'response-structure-error.xml',    'Testcase: application-access-control disabled, granted api user, invalid token => no access'],

            // Application-Access-Control enabled
            ['user-api-no-access',      true,  true,  400, 'response-structure-error.xml',     'Testcase: application-access-control enabled, invalid api user, valid token => NO access'],
            ['user-api-no-access',      true,  false, 400, 'response-structure-error.xml',     'Testcase: application-access-control enabled, invalid api user, invalid token => no access'],
            ['user-api-minimal-access', true,  true,  400, 'response-structure-error.xml',     'Testcase: application-access-control enabled, minimal api user, valid token => LIMITED access'],
            ['user-api-minimal-access', true,  false, 400, 'response-structure-error.xml',     'Testcase: application-access-control enabled, minimal api user, invalid token => no access'],
            ['user-api-grant-access',   true,  true,  200, 'response-structure-success.xml',   'Testcase: application-access-control enabled, granted api user, valid token => FULL access'],
            ['user-api-grant-access',   true,  false, 401, 'response-structure-error.xml',     'Testcase: application-access-control enabled, granted api user, invalid token => no access'],
        ];
    }

    /**
     * @dataProvider getTestScenes
     * @param string $userRefId
     * @param bool $enabledAccessControl
     * @param bool $validServiceToken
     * @param int $responseStatusCode
     * @param string $responseXmlFilename
     * @param $testCaseType
     */
    public function testSecurity($userRefId, $enabledAccessControl, $validServiceToken, $responseStatusCode, $responseXmlFilename, $testCaseType)
    {
        $application = $this->helperFactory->applicationAdd()->execute();
        $this->helperFactory->userPermissionAdd()->execute($application);

        $user = $this->helperFactory->userAdd()->execute();

        $this->helperFactory->userApplicationAdd()->execute($user, $application);

        $apiUser = $this->getContainer()->get('sso_webservice.database_webservice.user')->getUserByUsername($userRefId);
        $this->assertInstanceOf(\Sso\WebserviceBundle\Entity\Webservice\Type\User::class, $apiUser);

        $configuration = clone $this->configuration;
        $configuration->setUsername($apiUser->getUsername());
        if (!$validServiceToken) {
            $configuration->setServiceToken('x' . str_shuffle(substr($configuration->getServiceToken(), 1)));
            $configuration->setServiceToken('x' . str_shuffle(substr($configuration->getServiceToken(), 1)));
        }

        $requestBody = $this->getRequest($application, $user, $configuration);

        $headers = $enabledAccessControl ? [] : [ 'HTTP_X-DISABLE-APPLICATION-ACCESS-CONTROL' => 1 ];
        $response = $this->sendPostRequest('UserApplication', 'Show', $requestBody, $headers);

        static::assertEquals($responseStatusCode, $response->getStatusCode(), $this->fXml($requestBody, $response->getContent(), $testCaseType));

        $xml = $response->getContent();
        $xmlElement = $this->loadResponse($xml);

        $xmlElementExpected = $this->loadXmlDomElement(
            __DIR__ . '/xml/' . $responseXmlFilename,
            'SsoResponse'
        );

        static::assertEqualXMLStructure(
            $xmlElementExpected,
            $xmlElement,
            false,
            $this->fXml($requestBody, $xml, 'Xml structure check failed for ' . $testCaseType)
        );
    }

    /**
     * @param Application $application
     * @param User $user
     * @param Configuration $configuration
     * @return string
     */
    private function getRequest(Application $application, User $user, Configuration $configuration)
    {
        return '<?xml version="1.0" encoding="UTF-8"?>
<SsoRequest>' . (string)(new Credentials($configuration)) . '
    <UserApplication>
        <Show>
            <Key>
                <UserType>
                    <Username>' . $this->cdata($user->getUsername()) . '</Username>
                </UserType>
                <ApplicationType>
                    <Active>' . ($application->isActive() ? '1' : '0') . '</Active>
                </ApplicationType>
            </Key>
        </Show>
    </UserApplication>
</SsoRequest>';
    }
}
