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

/**
 * @category  Core
 * @package   Core_Security
 * @copyright Copyright (c) 2011. Burza d.o.o. (http://web.burza.hr/en/)
 * @license   proprietary
 */
class Core_Security extends Zend_Acl
{
    /**
     * @param array|Zend_Config $options
     */
    public function __construct($options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        } elseif ($options instanceof Zend_Config) {
            $this->setConfig($options);
        }
    }

    /**
     * @param array $roles
     *
     * @return \Core_Security
     */
    public function addRoles(array $roles)
    {
        foreach ($roles as $role) {
            if (isset($role['parent'])) {
                $parents = (array) $role['parent'];
            } else {
                $parents = null;
            }
            $this->addRole($role['name'], $parents);
        }
        return $this;
    }

    /**
     * @param array $resources
     *
     * @return \Core_Security
     */
    public function addResources(array $resources)
    {
        foreach ($resources as $resource => $config) {
            if (isset($config['parent'])) {
                $parent = $config['parent'];
            } else {
                $parent = null;
            }
            $this->addResource($resource, $parent);

            if (isset($config['privileges'])) {
                $this->addPrivileges($resource, $config['privileges']);
            }
        }
        return $this;
    }

    /**
     * @param string $resource
     * @param array  $privileges
     *
     * @return \Core_Security
     */
    public function addPrivileges($resource, array $privileges)
    {
        foreach($privileges as $type => $privileges) {
            $type = strtolower($type);
            foreach ($privileges as $role => $privilege) {
                if ('' === $privilege) {
                    throw new LogicException(sprintf(
                        'Privileges for role "%s" in security resource "%s" not specified.',
                        $role,
                        $resource
                    ));
                }
                if ('*' === $privilege) {
                    $privilege = null;
                } else {
                    $privilege = preg_split('/,\s*/', $privilege);
                }

                switch ($type) {
                    case 'allow':
                        $this->allow($role, $resource, $privilege);
                        break;
                    case 'deny':
                        $this->deny($role, $resource, $privilege);
                        break;
                }
            }
        }
        return $this;
    }

    /**
     * @param Core_Security_Role_Interface|Core_Security_Context_Role_Interface|string         $role      Role
     * @param Core_Security_Resource_Interface|Core_Security_Context_Resource_Interface|string $resource  Resource
     * @param string                                                                           $privilege Privilege
     *
     * @return boolean
     */
    public function isAllowed($role = null, $resource = null, $privilege = null)
    {
        if ($role instanceof Core_Security_Context_Role_Interface
            && $resource instanceof Core_Security_Context_Resource_Interface) {
            $roles = (array) $resource->getSecurityContextRoles($role);
        } elseif ($role instanceof Core_Security_Role_Interface) {
            $roles = (array) $role->getSecurityRoles();
        } else {
            $roles = (array) $role;
        }

        if ($resource instanceof Core_Security_Resource_Interface) {
            $resource  = $resource->getSecurityResource();
        }

        $isAllowed = false;
        foreach ($roles as $role) {
            if (self::TYPE_DENY === $this->_getRuleType(null !== $resource ? $this->get($resource) : null, new Zend_Acl_Role($role), $privilege)) {
                // privilege explicitly denied
                $isAllowed = false;
                break;
            } else if (self::TYPE_DENY === $this->_getRuleType(null !== $resource ? $this->get($resource) : null, new Zend_Acl_Role($role), null)) {
                // all privileges explicitly denied
                $isAllowed = false;
                break;
            }

            $isAllowed = parent::isAllowed($role, $resource, $privilege) || $isAllowed;
        }

        return $isAllowed;
    }

    /**
     * @param array $options
     *
     * @return Core_Security
     */
    public function setOptions(array $options)
    {
        if (isset($options['roles'])) {
            $this->addRoles($options['roles']);
            unset($options['roles']);
        }

        if (isset($options['resources'])) {
            $this->addResources($options['resources']);
            unset($options['resources']);
        }

        return $this;
    }

    /**
     * @param Zend_Config $config
     *
     * @return Core_Security
     */
    public function setConfig(Zend_Config $config)
    {
        return $this->setOptions($config->toArray());
    }
}
