<?php
/**
 * core.framework
 *
 * @category   Core
 * @package    Core_Security
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 */

/**
 * @category   Core
 * @package    Core_Security
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license    proprietary
 * @group      Core_Security
 */
class Core_SecurityTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var Core_Security
     */
    protected $_object;

    protected function setUp()
    {
        $this->_object = new Core_Security;
    }

    /**
     * @covers Core_Security::addRoles
     * @covers Core_Security::hasRole
     * @covers Core_Security::inheritsRole
     */
    public function testCanAddRoles()
    {
        $roles = array(
            array('name' => 'guest'),
            array('name' => 'user',  'parent' => 'guest'),
            array('name' => 'admin', 'parent' => 'user'),
        );
        $this->_object->addRoles($roles);

        $this->assertTrue($this->_object->hasRole('user'));
        $this->assertTrue($this->_object->inheritsRole('user', 'guest'));

        $this->assertTrue($this->_object->hasRole('admin'));
        $this->assertTrue($this->_object->inheritsRole('admin', 'guest')); // implicit (via user)
        $this->assertTrue($this->_object->inheritsRole('admin', 'user'));
    }

    /**
     * @covers Core_Security::addResources
     * @covers Core_Security::has
     * @covers Core_Security::inherits
     */
    public function testCanAddResources()
    {
        $resources = array(
            'frontend' => array(),
            'admin'    => array('parent' => 'frontend'),
        );
        $this->_object->addResources($resources);

        $this->assertTrue($this->_object->has('frontend'));
        $this->assertTrue($this->_object->has('admin'));
        $this->assertTrue($this->_object->inherits('admin', 'frontend'));
    }

    /**
     * @covers Core_Security::addPrivileges
     * @covers Core_Security::isAllowed
     */
    public function testCanAddPrivileges()
    {
        $roles = array(
            array('name' => 'guest'),
            array('name' => 'user',  'parent' => 'guest'),
            array('name' => 'admin', 'parent' => 'user'),
        );
        $this->_object->addRoles($roles);

        $resources = array(
            'frontend' => array(),
            'admin'    => array('parent' => 'frontend'),
        );
        $this->_object->addResources($resources);

        $privileges = array(
            'allow'    => array(
                'user'  => 'dashboard, logout',
                'admin' => '*',
            ),
            'deny'    => array(
                'user'  => 'something harmful',
            ),
        );
        // allow "user" to do "dashboard" and "logout" to "admin"
        $this->_object->addPrivileges('admin', $privileges);

        $this->assertTrue($this->_object->isAllowed('user', 'admin', 'logout'));
        $this->assertFalse($this->_object->isAllowed('user', 'admin', 'something harmful'));

        $this->assertTrue($this->_object->isAllowed('admin', 'admin'));
        $this->assertTrue($this->_object->isAllowed('admin', 'admin', 'whatever'));
    }

    /**
     * @covers Core_Security::addResources
     * @covers Core_Security::isAllowed
     */
    public function testCanAddPrivilegesViaAddingResources()
    {
        $roles = array(
            array('name' => 'user'),
        );
        $this->_object->addRoles($roles);

        $resources = array(
            'frontend' => array(),
            'admin'    => array(
                'privileges' => array(
                    'allow'    => array(
                        'user'  => 'logout',
                    ))
            ),
        );
        $this->_object->addResources($resources);

        $this->assertTrue($this->_object->isAllowed('user', 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::setOptions
     */
    public function testCanSetOptions()
    {
        $options = array(
            'roles'     => array(array('name' => 'user')),
            'resources' => array('admin' => array('privileges' => array('allow' => array('user' => 'logout')))),
        );
        $this->_object->setOptions($options);

        $this->assertTrue($this->_object->hasRole('user'));
        $this->assertTrue($this->_object->has('admin'));
        $this->assertTrue($this->_object->isAllowed('user', 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::__construct
     */
    public function testCanSetOptionsViaConstructor()
    {
        $options = array(
            'roles'     => array(array('name' => 'user')),
            'resources' => array('admin' => array('privileges' => array('allow' => array('user' => 'logout')))),
        );
        $object = new Core_Security($options);

        $this->assertTrue($object->hasRole('user'));
        $this->assertTrue($object->has('admin'));
        $this->assertTrue($object->isAllowed('user', 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::setConfig
     */
    public function testCanSetConfig()
    {
        $options = array(
            'roles'     => array(array('name' => 'user')),
            'resources' => array('admin' => array('privileges' => array('allow' => array('user' => 'logout')))),
        );
        $config = $this->getMock('Zend_Config', null, array($options));
        $this->_object->setConfig($config);

        $this->assertTrue($this->_object->hasRole('user'));
        $this->assertTrue($this->_object->has('admin'));
        $this->assertTrue($this->_object->isAllowed('user', 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::__construct
     */
    public function testCanSetConfigViaConstructor()
    {
        $options = array(
            'roles'     => array(array('name' => 'user')),
            'resources' => array('admin' => array('privileges' => array('allow' => array('user' => 'logout')))),
        );
        $config = $this->getMock('Zend_Config', null, array($options));
        $object = new Core_Security($config);

        $this->assertTrue($object->hasRole('user'));
        $this->assertTrue($object->has('admin'));
        $this->assertTrue($object->isAllowed('user', 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::isAllowed
     */
    public function testRoleCanBeInstanceOfSecurityRole()
    {
        $options = array(
            'roles'     => array(array('name' => 'dynamicRole')),
            'resources' => array('admin' => array('privileges' => array('allow' => array('dynamicRole' => 'logout')))),
        );
        $this->_object->setOptions($options);

        $role = $this->getMock('Core_Security_Role_Interface');
        $role
            ->expects($this->once())
            ->method('getSecurityRoles')
            ->will($this->returnValue('dynamicRole'));

        $this->assertTrue($this->_object->isAllowed($role, 'admin', 'logout'));
    }

    /**
     * @covers Core_Security::isAllowed
     */
    public function testResourceCanBeInstanceOfSecurityResource()
    {
        $options = array(
            'roles'     => array(array('name' => 'user')),
            'resources' => array(
                'dynamicResource' => array('privileges' => array('allow' => array('user' => 'logout')))
            ),
        );
        $this->_object->setOptions($options);

        $resource = $this->getMock('Core_Security_Resource_Interface');
        $resource
            ->expects($this->once())
            ->method('getSecurityResource')
            ->will($this->returnValue('dynamicResource'));

        $this->assertTrue($this->_object->isAllowed('user', $resource, 'logout'));
    }

    /**
     * @covers Core_Security::isAllowed
     */
    public function testRoleAndResourceCanBeSecurityContextInstances()
    {
        $options = array(
            'roles'     => array(array('name' => 'contextDynamicRole')),
            'resources' => array(
                'dynamicResource' => array('privileges' => array('allow' => array('contextDynamicRole' => 'logout')))
            ),
        );
        $this->_object->setOptions($options);

        $role     = $this->getMock('Core_Security_Context_Role_Interface');
        $resource = $this->getMock('Core_Security_Context_Resource_Interface');
        $resource
            ->expects($this->once())
            ->method('getSecurityResource')
            ->will($this->returnValue('dynamicResource'));
        $resource
            ->expects($this->once())
            ->method('getSecurityContextRoles')
            ->with($this->equalTo($role))
            ->will($this->returnValue('contextDynamicRole'));

        $this->assertTrue($this->_object->isAllowed($role, $resource, 'logout'));
    }
}
