<?php

/*******************************************************************************
 *
 * CoreAuthorisationHandler.php - Authorsiation handler
 *
 * Copyright (c) 2004-2016 NagVis Project (Contact: info@nagvis.org)
 *
 * License:
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ******************************************************************************/

/**
 * This class handles all authorisation tasks and is the glue between the
 * application and the different authorisation modules.
 *
 * @author Lars Michelsen <lm@larsmichelsen.com>
 */
class CoreAuthorisationHandler
{
    /** @var string */
    private $sModuleName = '';

    /** @var array */
    private $aPermissions = [];

    /** @var CoreAuthorisationModule */
    private $MOD;

    /**
     * FIXME: This is not really used anymore. It is only needed to hide the "hidden"
     * permissions from the user. Those hidden permissions are not used anymore. So
     * cleanup the auth DB and then remove this list.
     *
     * @var array[]
     */
    private $summarizePerms = [
        'MainCfg' => [
            'doEdit' => 'edit'
        ],
        'Map' => [
            'getMapProperties' => 'view',
            'getMapObjects' => 'view',
            'getObjectStates' => 'view',
            'doAdd' => 'add',
            'doEdit' => 'edit',
            'doRename' => 'edit',
            'doDelete' => 'edit',
            'modifyObject' => 'edit',
            'createObject' => 'edit',
            'deleteObject' => 'edit',
            'addModify' => 'edit',
            'editHtml' => 'edit',
        ],
        'Overview' => [
            'getOverviewRotations' => 'view',
            'getOverviewProperties' => 'view',
            'getOverviewMaps' => 'view',
            'getOverviewAutomaps' => 'view',
        ],
        'AutoMap' => [
            'getAutomapProperties' => 'view',
            'getAutomapObjects' => 'view',
            'parseAutomap' => 'view',
            'getObjectStates' => 'view',
            'doAdd' => 'add',
            'doEdit' => 'edit',
            'doRename' => 'edit',
            'doDelete' => 'edit',
            'modifyObject' => 'edit',
            'createObject' => 'edit',
            'deleteObject' => 'edit',
        ],
        'ManageShapes' => [
            'view' => 'manage',
            'doUpload' => 'manage',
            'doDelete' => 'manage',
        ],
        'ManageBackgrounds' => [
            'view' => 'manage',
            'doUpload' => 'manage',
            'doCreate' => 'manage',
            'doDelete' => 'manage',
        ],
        'ChangePassword' => [
            'view' => 'change',
        ],
        'UserMgmt' => [
            'view' => 'manage',
            'getUserRoles' => 'manage',
            'getAllRoles' => 'manage',
            'doAdd' => 'manage',
            'doEdit' => 'manage',
            'doDelete' => 'manage',
        ],
        'RoleMgmt' => [
            'view' => 'manage',
            'getRolePerms' => 'manage',
            'doAdd' => 'manage',
            'doEdit' => 'manage',
            'doDelete' => 'manage',
        ]
    ];

    public function __construct()
    {
        $this->sModuleName = cfg('global', 'authorisationmodule');
        $this->MOD = new $this->sModuleName();
    }

    /**
     * @param string $old_name
     * @param string $new_name
     * @return bool
     */
    public function renameMapPermissions($old_name, $new_name)
    {
        return $this->MOD->renameMapPermissions($old_name, $new_name);
    }

    /**
     * @param string $mod
     * @param string $name
     * @return bool
     */
    public function createPermission($mod, $name)
    {
        return $this->MOD->createPermission($mod, $name);
    }

    /**
     * @param string $mod
     * @param string $name
     * @return bool
     */
    public function deletePermission($mod, $name)
    {
        return $this->MOD->deletePermission($mod, $name);
    }

    /**
     * @return string
     */
    public function getModule()
    {
        return $this->sModuleName;
    }

    /**
     * @return bool
     */
    public function rolesConfigurable()
    {
        return $this->MOD->rolesConfigurable;
    }

    /**
     * @param int $roleId
     * @return bool
     */
    public function deleteRole($roleId)
    {
        // FIXME: First check if this is supported

        return $this->MOD->deleteRole($roleId);
    }

    /**
     * @param int $roleId
     * @return array|false
     */
    public function roleUsedBy($roleId)
    {
        return $this->MOD->roleUsedBy($roleId);
    }

    /**
     * @param int $userId
     * @return bool
     */
    public function deleteUser($userId)
    {
        // FIXME: First check if this is supported

        return $this->MOD->deleteUser($userId);
    }

    /**
     * @param int $userId
     * @param array $roles
     * @return bool
     */
    public function updateUserRoles($userId, $roles)
    {
        // FIXME: First check if this is supported

        return $this->MOD->updateUserRoles($userId, $roles);
    }

    /**
     * @param int $userId
     * @return array
     */
    public function getUserRoles($userId)
    {
        // FIXME: First check if this is supported

        return $this->MOD->getUserRoles($userId);
    }

    /**
     * @return array
     */
    public function getAllRoles()
    {
        // FIXME: First check if this is supported

        return $this->MOD->getAllRoles();
    }

    /**
     * @param array $a
     * @param array $b
     * @return int
     */
    private function sortPerms($a, $b)
    {
        return strcmp($a['mod'] . $a['obj'] . $a['act'], $b['mod'] . $b['obj'] . $b['act']);
    }

    /**
     * @return void
     * @throws NagVisException
     */
    public function cleanupPermissions()
    {
        global $CORE;

        // loop all map related permissions and check whether or not the map
        // is still available
        foreach ($this->getAllVisiblePerms() as $perm) {
            if ($perm['mod'] == 'Map' && $perm['obj'] != '*') {
                if (count($CORE->getAvailableMaps('/^' . $perm['obj'] . '$/')) <= 0) {
                    $this->deletePermission('Map', $perm['obj']);
                }
            }
        }
    }

    /**
     * @return array
     */
    public function getAllVisiblePerms()
    {
        $aReturn = [];
        // FIXME: First check if this is supported

        $aPerms = $this->MOD->getAllPerms();

        // Filter perms to only display the visible ones
        foreach ($aPerms as $perm) {
            if (
                !isset($this->summarizePerms[$perm['mod']])
                || (
                    isset($this->summarizePerms[$perm['mod']])
                    && !isset($this->summarizePerms[$perm['mod']][$perm['act']])
                )
            ) {
                $aReturn[] = $perm;
            }
        }

        usort($aReturn, [$this, 'sortPerms']);

        return $aReturn;
    }

    /**
     * @param string $name
     * @return bool
     */
    public function checkRoleExists($name)
    {
        // FIXME: First check if this is supported

        return $this->MOD->checkRoleExists($name);
    }

    /**
     * @param string $name
     * @return bool
     */
    public function createRole($name)
    {
        // FIXME: First check if this is supported

        return $this->MOD->createRole($name);
    }

    /**
     * @param int $roleId
     * @return array
     */
    public function getRolePerms($roleId)
    {
        // FIXME: First check if this is supported

        return $this->MOD->getRolePerms($roleId);
    }

    /**
     * @param string $sName
     * @return int|string
     */
    public function getUserId($sName)
    {
        // FIXME: First check if this is supported
        return $this->MOD->getUserId($sName);
    }

    /**
     * @param string $sName
     * @return int|false
     */
    public function getRoleId($sName)
    {
        // FIXME: First check if this is supported
        return $this->MOD->getRoleId($sName);
    }

    /**
     * @param int $roleId
     * @param array $perms
     * @return bool
     */
    public function updateRolePerms($roleId, $perms)
    {
        // FIXME: First check if this is supported
        return $this->MOD->updateRolePerms($roleId, $perms);
    }

    /**
     * @param string $sUsername
     * @return array
     */
    public function parsePermissions($sUsername = null)
    {
        $this->aPermissions = $this->MOD->parsePermissions($sUsername);
        return $this->aPermissions;
    }

    /**
     * @param string $sModule
     * @param string $sAction
     * @param string $sObj
     * @return bool
     */
    public function isPermitted($sModule, $sAction, $sObj = null)
    {
        // Module access?
        $access = [];
        if (isset($this->aPermissions[$sModule])) {
            $access[$sModule] = [];
        }
        if (isset($this->aPermissions[AUTH_PERMISSION_WILDCARD])) {
            $access[AUTH_PERMISSION_WILDCARD] = [];
        }

        if (count($access) > 0) {
            // Action access?
            foreach ($access as $mod => $acts) {
                if (isset($this->aPermissions[$mod][$sAction])) {
                    $access[$mod][$sAction] = [];
                }
                if (isset($this->aPermissions[$mod][AUTH_PERMISSION_WILDCARD])) {
                    $access[$mod][AUTH_PERMISSION_WILDCARD] = [];
                }
            }

            if (count($access[$mod]) > 0) {
                // Don't check object permissions
                if ($sObj === null) {
                    return true;
                }

                // Object access?
                foreach ($access as $mod => $acts) {
                    foreach ($acts as $act => $objs) {
                        if (isset($this->aPermissions[$mod][$act][$sObj])) {
                            return true;
                        } elseif (isset($this->aPermissions[$mod][$act][AUTH_PERMISSION_WILDCARD])) {
                            return true;
                        } elseif (DEBUG && DEBUGLEVEL & 2) {
                            debug(
                                'Object access denied (Mod: ' . $sModule . ' Act: ' . $sAction . ' Object: ' . $sObj
                            );
                        }
                    }
                }
            } elseif (DEBUG && DEBUGLEVEL & 2) {
                debug('Action access denied (Mod: ' . $sModule . ' Act: ' . $sAction . ' Object: ' . $sObj);
            }
        } elseif (DEBUG && DEBUGLEVEL & 2) {
            debug('Module access denied (Mod: ' . $sModule . ' Act: ' . $sAction . ' Object: ' . $sObj);
        }

        return false;
    }
}
