<?php

defined('BASEPATH') or exit('No direct script access allowed');

if (!class_exists('Controller')) {
    class Controller extends CI_Controller {}
}

class Auth extends cf_controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->library(['form_validation']);
        $this->lang->load('cf_message');
        //list of errors wrapped in <p> class of errors
        $this->form_validation->set_error_delimiters('<span class="errorlist">', '</span>');
        //$this->load->database();
        $this->load->helper('url');
        $this->load->model('ProfileModel');
        $this->carabiner->js('../themes/default/bootstrap/cfengine/js/cfe-paginator-dropdown.js');
        $this->config->load('ion_auth', true);
        if ($this->uri->segment(2) !== 'permission_deny') {
            $this->addRolesData($this->data);
        }
    }

    //redirect if needed, otherwise display the user list
    public function index()
    {
        $getparams = $this->uri->uri_to_assoc(3);
        $showUsers = isset($getparams['users']) ? $getparams['users'] : 'all';
        $page = isset($getparams['page']) ? $getparams['page'] : 1;
        $count = isset($getparams['rows']) ? $getparams['rows'] : 25;

        $this->session->set_userdata('userListPage', $page);
        $this->session->set_userdata('usersPerPage', $count);
        $this->session->set_userdata('userListType', $showUsers);

        if (!$this->ion_auth->logged_in()) {
            //redirect them to the login page
            redirect('login/index', 'refresh');
        }
        $this->data['title'] = $this->lang->line('mission_portal_title') . ' - Admin';
        $this->data['username'] = $this->session->userdata('username');
        //list the users
        $this->data['userroles'] = $this->session->userdata('roles');
        $this->data['is_admin'] = $this->ion_auth->is_admin();

        try {
            $users = $this->ion_auth->get_users_array($page, $count, $showUsers, true);
            $this->data['users'] = $users['data'];
            $this->data['totalUsers'] = $users['meta']['total'];
        } catch (Exception $e) {
            $this->_set_error_from_exception($e);
        }

        $this->data['showUsers'] = $showUsers;
        $this->data['number_of_rows'] = $count;
        $this->data['current'] = $page;

        $this->data['message'] = validation_errors()
            ? '<p class="error">' . validation_errors() . '</p>'
            : ($this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->session->flashdata('message'));
        if (is_ajax()) {
            $this->load->view('auth/user_list', $this->data);
        } else {
            redirect($this->config->item('base_url'), 'refresh');
        }
    }

    /**
     * @rbacName View settings page
     * @rbacGroup User/Role management
     * @rbacAlias auth.admin_page
     */
    public function admin_page()
    {
        if (!$this->ion_auth->logged_in()) {
            //redirect them to the login page
            redirect('login/index', 'refresh');
        }

        $getparams = $this->uri->uri_to_assoc(3);
        $showUsers = isset($getparams['users']) ? $getparams['users'] : 'internal';

        $page = isset($getparams['page'])
            ? $getparams['page']
            : ($this->input->post('page')
                ? $this->input->post('page')
                : 1);
        $count = isset($getparams['rows'])
            ? $getparams['rows']
            : ($this->input->post('rows')
                ? $this->input->post('rows')
                : 25);

        $requiredjs = [
            ['dist/widgets.bundle.js'],
            ['../../../themes/default/bootstrap/cfengine/js/cfe-paginator-dropdown.js'],
        ];

        $this->carabiner->js($requiredjs);

        $this->data['title'] = $this->lang->line('mission_portal_title') . ' - Admin';
        $this->data['title_header'] = 'Admin';

        $this->data['username'] = $this->session->userdata('username');
        $this->data['message'] = validation_errors()
            ? '<p class="error">' . validation_errors() . '</p>'
            : $this->session->flashdata('message');

        // get system setting for RBAC, if it is OFF - show warning message on users and roles page
        $this->data['rbac_setting'] = $this->setting_lib->get_rbac_setting();

        $this->data['show2FaEnforcementHint'] = false;
        if (isActionAllowed('settings.manage')) {
            // show only for users who can manage preferences
            try {
                $this->load->model(['TwoFaModel', 'settings_rest_model']);
                $appSettings = $this->settings_rest_model->get_app_settings();
                $usersStatus = $this->TwoFaModel->getUsersStatus();
                // show hint only if 2FA is not enforced and there are users without 2FA enabled
                $this->data['show2FaEnforcementHint'] =
                    empty($appSettings['enforce2FA']) && empty($usersStatus['allEnabled']);
            } catch (Exception $e) {
                log_message(
                    log_level_for_exception($e),
                    'admin_page(): Unable to determine 2FA users status. Error: ' . $e->getMessage(),
                );
            }
        }

        try {
            if ($this->input->post('search')) {
                // search
                $users = $this->ion_auth->search_user($showUsers, $this->input->post('search'), true);
                $this->data['search'] = $this->input->post('search');
            } else {
                // list the users
                $users = $this->ion_auth->get_users_array($page, $count, $showUsers, true, determineInactive: true);
            }

            $this->data['users'] = $users['data'];
            $this->data['totalUsers'] = $users['meta']['total'];
        } catch (Exception $e) {
            if ($showUsers == 'external') {
                // change error message while getting external users
                $e = new Exception('An error occurred while getting external users, this may be the result of
                                    a misconfiguration in <a href="/settings/authentication">Authentication settings</a>,
                                    please review.');
            }
            $this->_set_error_from_exception($e);
        }

        $this->data['showUsers'] = $showUsers;
        $this->data['number_of_rows'] = $count;
        $this->data['current'] = $page;

        $this->data['userroles'] = $this->session->userdata('roles');

        $this->session->set_userdata('userListPage', $page);
        $this->session->set_userdata('usersPerPage', $count);
        $this->session->set_userdata('userListType', $showUsers);

        $this->data['is_admin'] = $this->ion_auth->is_admin();
        if (is_ajax()) {
            $this->load->view('auth/user_list', $this->data);
        } else {
            $this->template->load('template', 'auth/index', $this->data);
        }
    }

    public function change_password($id = null)
    {
        $this->form_validation->set_rules('old', 'Old password', 'required');
        $this->form_validation->set_rules(
            'new',
            'New Password',
            'required|min_length[' .
                $this->config->item('min_password_length', 'ion_auth') .
                ']|max_length[' .
                $this->config->item('max_password_length', 'ion_auth') .
                ']|matches[new_confirm]|differs[old]',
        );
        $this->form_validation->set_rules('new_confirm', 'Confirm New Password', 'required');
        $this->form_validation->set_rules('twoFaCode', 'twoFaCode', 'xss_clean');
        $this->data['userrole'] = $this->session->userdata('role');
        $this->data['is_admin'] = $this->ion_auth->is_admin();

        if (!$this->ion_auth->logged_in()) {
            redirect('login/index', 'refresh');
        }

        $data = [];

        if ($this->form_validation->run() == false) {
            $this->data['message'] = validation_errors() ? validation_errors() : $this->session->flashdata('message');
            // should n't redraw entire form
            if (is_ajax() && $this->data['message'] != '') {
                $data['responseText'] = $this->data['message'];
                $data['status'] = 'validation_error';
            }
        } else {
            $user_id = $id ?? $this->session->userdata('username');

            // if the user cannot change password for others, then set user to the current username
            if (!isActionAllowed('auth.change_password')) {
                $user_id = $this->session->userdata('username');
            }

            $change = $this->ion_auth->change_password(
                $user_id,
                $this->input->post('old'),
                $this->input->post('new'),
                $this->input->post('twoFaCode'),
            );
            if ($change) {
                $this->load->library('encryption');
                $newPass = $this->input->post('new');
                $encryptedPass = $this->encryption->encrypt($newPass);
                $this->session->set_userdata('password', $encryptedPass);
                $this->ProfileModel->setAdditionalData(
                    $user_id,
                    ProfileModel::FIRST_lOGIN_AFTER_RESET_KEY,
                    data: false,
                );
                $data['status'] = 'all_ok';
            } else {
                $data['status'] = 'invalid_password';
            }
        }

        if (is_ajax()) {
            if ($this->ion_auth->errors()) {
                $data['message']['alert-error'] = $this->ion_auth->errors();
            }

            if ($this->ion_auth->infos()) {
                $data['message']['alert-warning'] = $this->ion_auth->infos();
            }

            if ($this->ion_auth->messages()) {
                $data['message']['alert-success'] = $this->ion_auth->messages();
            }

            if ($this->session->userdata('defaultadmin') && $data['status'] == 'all_ok') {
                $this->session->unset_userdata('defaultadmin');
            }

            if (
                $data['status'] === 'invalid_password' &&
                !str_contains($this->ion_auth->errors(), Ion_auth::TWO_FACTOR_UNSUCCESSFUL_ERROR_MESSAGE)
            ) {
                $data['message']['alert-error'] .= ' - ' . strtolower($this->lang->line('org_passwd_valid'));
            }
            $result = json_encode($data);
            respond_ok($result);
            return;
        }

        $this->data['message'] .= $this->ion_auth->infos();
        $this->load->view('auth/change_password', $this->data);
    }

    //reset password - final step for forgotten password
    public function reset_password($code)
    {
        $reset = $this->ion_auth->forgotten_password_complete($code);
        if ($reset) {
            //if the reset worked then send them to the login page
            $this->session->set_flashdata('message', $this->ion_auth->messages());
            redirect('login/index', 'refresh');
        } else {
            //if the reset didnt work then send them back to the forgot password page
            $this->session->set_flashdata('message', $this->ion_auth->errors());
            redirect('auth/forgot_password', 'refresh');
        }
    }

    public function password_form()
    {
        $profile = $this->ProfileModel->getProfileData($this->session->userdata('username'));
        $js = [['changePassword.js']];
        $this->carabiner->js(dev_file: $js, prod_file: $js, combine: true, minify: true, group: 'change_password');
        $this->data = [...$this->setting_lib->getSecuritySettings(), ...$this->data];
        $this->data['two_factor_enabled'] = $profile['two_factor_enabled'];
        $this->load->view('auth/change_password', $this->data);
    }

    /**
     * create a new user
     *
     * @rbacName Create user
     * @rbacGroup User/Role management
     * @rbacAlias auth.create_user
     */
    public function create_user()
    {
        $this->data['title'] = 'Create User';
        $this->data['user_type'] = 'internal';
        $this->data['defaultRole'] = $this->ion_auth->get_default_role();

        //validate form input
        $this->form_validation->set_rules('id', 'User Name', 'required|xss_clean|alpha_dash_dots');
        $this->form_validation->set_rules('email', 'Email Address', 'required|valid_email');
        $this->form_validation->set_rules(
            'password',
            'Password',
            'required|min_length[' .
                $this->setting_lib->getSecuritySettings()[Settings_rest_model::MIN_PASSWORD_LENGTH] .
                ']|max_length[' .
                $this->config->item('max_password_length', 'ion_auth') .
                ']|matches[password_confirm]',
        );
        $this->form_validation->set_rules('password_confirm', 'Password Confirmation', 'required');
        $this->form_validation->set_rules('roles[]', 'role', 'xss_clean');

        if ($this->form_validation->run() == true) {
            $data = [
                'id' => $this->input->post('id'),
                'email' => $this->input->post('email'),
                'password' => $this->input->post('password'),
                'roles' => $this->input->post('roles_list'),
            ];
        }

        // show error about user presence in case when not overwrite and user exists
        if (
            $this->input->post('overwrite') === '0' &&
            ($this->input->post('id') && $this->ion_auth->get_user($this->input->post('id')))
        ) {
            $data['status'] = 'user_exists_error';
            $data['responseText'] = [
                'type' => 'alert-error',
                'content' => 'User with this name already exists. Please provide a new name or overwrite.',
            ];

            $result = json_encode($data);
            respond_ok($result);
            return;
        }

        if ($this->form_validation->run() == true && $this->ion_auth->register($data)) {
            //check to see if we are creating the user
            //redirect them back to the admin page
            if (is_ajax()) {
                $this->data['message'] = $this->ion_auth->messages();
                $this->data['userrole'] = $this->session->userdata('role');
                $this->data['is_admin'] = $this->ion_auth->is_admin();

                $page = $this->session->userdata('userListPage');
                $count = $this->session->userdata('usersPerPage');
                $showUsers = $this->session->userdata('userListType');

                $users = $this->ion_auth->get_users_array($page, $count, $showUsers, true);

                $this->data['users'] = $users['data'];
                $this->data['totalUsers'] = $users['meta']['total'];
                $this->data['showUsers'] = $showUsers;
                $this->data['current'] = $page;
                $this->data['number_of_rows'] = $count;
                $this->data['add_type'] = 'user';

                // on success - return json - we will  not redraw entire form
                $data['status'] = 'all_ok';
                $data['responseText'] = $this->load->view('auth/user_list', $this->data, true);

                $result = json_encode($data);
                respond_ok($result);
                return;
            } else {
                $this->session->set_flashdata('message', 'User Created');
                redirect('auth', 'refresh');
            }
        } else {
            //display the create user form
            //set the flash data error message if there is one
            //
            //
            //
            $updateErrorContent = $this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->session->flashdata('message');
            $updateErrors = !empty($updateErrorContent)
                ? [
                    'type' => 'alert-error',
                    'content' => $updateErrorContent,
                ]
                : '';
            $this->data['message'] = validation_errors() ? validation_errors('<label>', '</label>') : $updateErrors;

            // on error - return json - we will  not redraw entire form
            if ($this->data['message'] != '') {
                $data['status'] = 'validation_error';
                $data['responseText'] = $this->data['message'];

                $result = json_encode($data);
                respond_ok($result);
                return;
            }

            $this->data['id'] = [
                'name' => 'id',
                'id' => 'id',
                'type' => 'text',
                'value' => $this->form_validation->set_value('id'),
            ];

            $this->data['email'] = [
                'name' => 'email',
                'id' => 'email',
                'type' => 'text',
                'value' => $this->form_validation->set_value('email'),
            ];
            $this->data['password'] = [
                'name' => 'password',
                'id' => 'password',
                'type' => 'password',
                'value' => $this->form_validation->set_value('password'),
            ];
            $this->data['password_confirm'] = [
                'name' => 'password_confirm',
                'id' => 'password_confirm',
                'type' => 'password',
                'value' => $this->form_validation->set_value('password_confirm'),
            ];

            $selected_roles = $this->input->post('roles_list');

            // create data for checkbox
            if (!empty($selected_roles)) {
                foreach ($selected_roles as $role) {
                    $this->data['user_roles'][$role] = [
                        'name' => 'roles[]',
                        'value' => $role,
                        'checked' => $this->form_validation->set_checkbox('roles[]', $role),
                    ];
                }
            } else {
                $this->data['user_roles'] = '';
            }

            $this->data['roles'] = $this->ion_auth->get_roles(); // will return all available roles

            $this->data['op'] = 'create';
            $this->load->view('auth/add_edit_user', $this->data);
        }
    }

    public function _get_csrf_nonce()
    {
        $this->load->helper('string');
        $key = random_string('alnum', 8);
        $value = random_string('alnum', 20);
        $this->session->set_flashdata('csrfkey', $key);
        $this->session->set_flashdata('csrfvalue', $value);

        return [$key => $value];
    }

    public function _valid_csrf_nonce()
    {
        if (
            $this->input->post($this->session->flashdata('csrfkey')) !== false &&
            $this->input->post($this->session->flashdata('csrfkey')) == $this->session->flashdata('csrfvalue')
        ) {
            return true;
        } else {
            return false;
        }
    }

    public function view_profile()
    {
        $userdata = $this->ion_auth->get_user($this->session->userdata('username'));
        $this->data['user'] = $userdata;
        $this->data['title'] = $this->lang->line('mission_portal_title') . ' - Profile';
        $this->data['title_header'] = 'Profile';
        $this->data['username'] = $this->session->userdata('username');
        $this->template->load('templates/singleColumnTemplate', 'auth/view_profile', $this->data);
    }

    /**
     * @rbacName Edit user
     * @rbacGroup User/Role management
     * @rbacAlias auth.edit_user
     */
    public function edit_user($id)
    {
        $this->data['title'] = 'Edit User';
        $getparams = $this->uri->uri_to_assoc(3);

        $user = $this->ion_auth->get_user($id);

        if ($user['external']) {
            $this->data['user_type'] = 'external';
        } else {
            $this->data['user_type'] = 'internal';
        }

        $required_if_internal = $user['external'] ? '' : 'required|';

        if (!$this->ion_auth->logged_in()) {
            redirect('auth', 'refresh');
        }

        if (!isActionAllowed('user.post')) {
            redirect('auth/permission_deny', 'refresh');
        }

        //validate form input
        $this->form_validation->set_rules('id', 'User name', 'required|xss_clean|alpha_dash_dots');
        $this->form_validation->set_rules('email', 'Email Address', $required_if_internal . 'valid_email');
        $this->form_validation->set_rules('roles[]', 'Role', 'xss_clean');
        $this->form_validation->set_rules('twoFaCode', 'twoFaCode', 'xss_clean');

        $roles = $this->input->post('roles_list');
        if ($roles === false || $roles === null) {
            $roles = [];
        }
        $data = [
            // 'id'    => $this->input->post('id'), // not supported by API
            'email' => $this->input->post('email'),
            'roles' => $roles,
        ];

        if (
            $this->form_validation->run() == true &&
            $this->ion_auth->update_user($id, $data, $this->input->post('twoFaCode'))
        ) {
            if ($this->input->post('reset_password')) {
                $resetPasswordResult = $this->ion_auth->reset_password($this->input->post('id'));

                if (!$resetPasswordResult) {
                    $data['status'] = 'validation_error';
                    $data['responseText'] = [
                        'type' => 'alert-error',
                        'content' => $this->ion_auth->errors(),
                    ];
                    $result = json_encode($data);
                    respond_ok($result);
                    return;
                }
            }

            // update the session roles so that user doesnot have to log in again for roles to be effective
            // If roles are retrieved from session for the logged in user
            $session_data = ['roles' => $roles];
            if ($id == $this->session->userdata('username')) {
                $this->session->set_userdata($session_data);
            }

            if (is_ajax()) {
                if ($this->ion_auth->errors()) {
                    $data['message']['alert-error'] = $this->ion_auth->errors();
                }

                if ($this->ion_auth->infos()) {
                    $data['message']['alert-warning'] = $this->ion_auth->infos();
                }

                if ($this->ion_auth->messages()) {
                    $data['message']['alert-success'] = $this->ion_auth->messages();
                }

                $page = $this->session->userdata('userListPage');
                $count = $this->session->userdata('usersPerPage');
                $showUsers = $this->session->userdata('userListType');

                $this->data['is_admin'] = $this->ion_auth->is_admin();
                $users = $this->ion_auth->get_users_array($page, $count, $showUsers, true);
                $this->data['users'] = $users['data'];
                $this->data['totalUsers'] = $users['meta']['total'];
                $this->data['showUsers'] = $showUsers;
                $this->data['current'] = $page;
                $this->data['number_of_rows'] = $count;

                // on success - return json - we will  not redraw entire form
                $data['status'] = 'all_ok';
                $data['responseText'] = $this->load->view('auth/user_list', $this->data, true);

                $result = json_encode($data);
                respond_ok($result);
                return;
            } else {
                $this->session->set_flashdata('message', 'User Updated');
                redirect('auth', 'refresh');
            }
        } else {
            //display the edit user form
            //set the flash data error message if there is one
            $updateErrorContent = $this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->session->flashdata('message');
            $updateErrors = !empty($updateErrorContent)
                ? [
                    'type' => 'alert-error',
                    'content' => $updateErrorContent,
                ]
                : '';

            $this->data['message'] = validation_errors() ? validation_errors() : $updateErrors;

            // on error - return json - we will  not redraw entire form
            if ($this->data['message'] != '') {
                $data['status'] = 'validation_error';
                $data['responseText'] = $this->data['message'];

                $result = json_encode($data);
                respond_ok($result);
                return;
            }

            $this->data['id'] = [
                'name' => 'id',
                'id' => 'id',
                'type' => 'text',
                'readonly' => true, // edit user not supported by API
                'value' => $this->form_validation->set_value('id', $user['id']),
            ];

            $this->data['email'] = [
                'name' => 'email',
                'id' => 'email',
                'type' => 'text',
                'value' => $this->form_validation->set_value('email', key_exists('email', $user) ? $user['email'] : ''),
            ];

            //get user roles
            $assigned_roles = $this->ion_auth->get_user_role($id);

            // create data for checkbox
            if (!empty($assigned_roles)) {
                foreach ($assigned_roles as $role) {
                    $this->data['user_roles'][$role] = [
                        'name' => 'roles[]',
                        'value' => $role,
                        'checked' => $this->form_validation->set_checkbox('roles[]', $role),
                    ];
                }
            } else {
                $this->data['user_roles'] = '';
            }

            $profile = $this->ProfileModel->getProfileData($this->session->userdata('username'));
            $editedUsersProfile = $this->ProfileModel->getProfileData($id);
            $this->data['two_factor_enabled'] = $profile['two_factor_enabled'];
            $this->data['two_factor_enabled_for_user'] = $editedUsersProfile['two_factor_enabled'];
            $this->data['locked'] = $user['locked'];

            $this->data['roles'] = $this->ion_auth->get_roles();

            $this->data['op'] = 'edit';
            $this->load->view('auth/add_edit_user', $this->data);
        }
    }

    public function _get_roles_not_assigned_to_user($assignedRoles = [])
    {
        $roles = $this->ion_auth->get_roles();
        // get roles list, only roles which NOT assigned to user
        if (!empty($assignedRoles)) {
            //prepare data
            $assignedRoles_tmp = [];

            foreach ($assignedRoles as $role) {
                $assignedRoles_tmp[$role] = [
                    'id' => $role,
                ];
            }
            //create roles array, which will look as [key] = value
            $roles_tmp = [];
            foreach ($roles as $item => $role) {
                //$roles_tmp[$role['name']] = $role['name'];
                $roles_tmp[$role['id']] = [
                    'id' => $role['id'],
                ];
            }

            //diff data
            $roles = arrayRecursiveDiff($roles_tmp, $assignedRoles_tmp);
        }

        $available_roles = [];

        foreach ($roles as $role) {
            $available_roles[$role['id']] = [
                'id' => $role['id'],
            ];
        }
        return $available_roles;
    }

    /**
     * @rbacName Create role
     * @rbacGroup User/Role management
     * @rbacAlias auth.manage_role
     */
    public function manage_role($op = false, $id = false)
    {
        if (!$this->ion_auth->logged_in()) {
            redirect('auth', 'refresh');
        }

        $this->data['is_admin'] = $this->ion_auth->is_admin();

        if (!empty($op)) {
            $this->data['title'] = 'Create role';
            $this->data['operation'] = 'Create';

            $this->form_validation->set_rules('id', 'Name', 'required|xss_clean');

            $this->form_validation->set_rules('description', 'Description', 'required|xss_clean|trim');

            $this->form_validation->set_rules('crxi[]', 'Include classes', 'required|xss_clean');

            $this->form_validation->set_rules('crxx[]', 'Exclude classes', 'xss_clean');

            if ($this->form_validation->run($this) == true) {
                $data = [
                    'name' => $this->input->post('id'),
                    'description' => $this->input->post('description'),
                    'includeContext' => implode(',', array_filter(array_unique((array) $this->input->post('crxi')))),
                    'includeContext' => implode('|', array_filter(array_unique((array) $this->input->post('crxi')))),
                    'excludeContext' => implode('|', array_filter(array_unique((array) $this->input->post('crxx')))),
                ];

                if ($op == 'edit' && !$this->ion_auth->update_role($id, $data)) {
                    $this->__load_role_add_edit($op, $id);
                    return;
                }

                if ($op == 'create' && $this->ion_auth->create_role($data) === false) {
                    $this->__load_role_add_edit($op, $id);
                    return;
                }

                if (is_ajax()) {
                    $this->data['message'] = $this->ion_auth->messages();
                    $this->data['roles'] = $this->ion_auth->get_roles($this->session->userdata('username'));
                    $this->data['defaultRole'] = $this->ion_auth->get_default_role();
                    // on success - return json - we will  not redraw entire form
                    $data['status'] = 'all_ok';
                    $data['responseText'] = $this->load->view('auth/list_role', $this->data, true);

                    $result = json_encode($data);
                    respond_ok($result);
                    return;
                } else {
                    $this->session->set_flashdata('message', $this->ion_auth->messages());
                    redirect('auth/manage_role', 'refresh');
                }
            } else {
                $this->__load_role_add_edit($op, $id);
            }
        } else {
            try {
                $this->data['roles'] = $this->ion_auth->get_roles();
                $this->data['defaultRole'] = $this->ion_auth->get_default_role();
            } catch (Exception $e) {
                $this->_set_error_from_exception($e);
            }

            $this->data['message'] = validation_errors()
                ? validation_errors()
                : ($this->ion_auth->errors()
                    ? $this->ion_auth->errors()
                    : $this->session->flashdata('message'));
            $this->load->view('auth/list_role', $this->data);
        }
    }

    /**
     * @rbacName Set default role
     * @rbacGroup User/Role management
     * @rbacAlias auth.setDefaultRole
     */
    public function setDefaultRole($name = '')
    {
        return $this->ion_auth->set_default_role($name);
    }

    public function _set_error_from_exception($e)
    {
        switch ($e) {
            case $e instanceof HttpClient_Forbidden:
                $this->ion_auth->set_error($this->lang->line('REST_pest_forbidden'));
                break;

            default:
                $this->ion_auth->set_error($e->getMessage());
        }

        if ($this->ion_auth->errors()) {
            $this->data['sys_messages']['alert-error'] = $this->ion_auth->errors();
        }

        if ($this->ion_auth->infos()) {
            $this->data['sys_messages']['alert-warning'] = $this->ion_auth->infos();
        }

        if ($this->ion_auth->messages()) {
            $this->data['sys_messages']['alert-success'] = $this->ion_auth->messages();
        }
    }

    /**
     *Function to validate include classes for roles. Each role MUST have one class into include(crxi) list
     * @param type $crxi
     * @return boolean
     */
    public function includeClassValidate($crxi)
    {
        $crxi = array_filter(array_unique($crxi));

        foreach ($crxi as $item => $value) {
            if (!empty($value)) {
                return true;
            }
        }

        $this->form_validation->set_message('includeClassValidate', 'Role must have at least one include class');
        return false;
    }

    public const UNEDITABLE_ROLES = ['admin', 'cf_remoteagent'];
    public function __load_role_add_edit($op, $id)
    {
        $id = rawurldecode($id);
        $this->data['uneditable'] = in_array($id, self::UNEDITABLE_ROLES);
        $this->load->helper('create_html_list_from_string');
        $this->data['message'] = validation_errors()
            ? validation_errors()
            : ($this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->session->flashdata('message'));
        // on error - return json - we will  not redraw entire form
        if ($this->data['message'] != '') {
            $data['status'] = 'validation_error';
            $data['responseText'] = $this->data['message'];

            $result = json_encode($data);
            respond_ok($result);
            return;
        }

        $this->data['id'] = [
            'name' => 'id',
            'id' => 'id',
            'type' => 'text',
            'value' =>
                $this->form_validation->set_value('id') == null && $id != null
                    ? $id
                    : $this->form_validation->set_value('id'),
        ];
        $this->data['description'] = [
            'name' => 'description',
            'id' => 'description',
            'type' => 'text',
            'rows' => '3',
            'cols' => '20',
            'value' => $this->form_validation->set_value('description'),
        ];
        $this->data['crxi'] = [
            'name' => 'crxi',
            'id' => 'crxi',
            'type' => 'text',
            'value' => $this->form_validation->set_value('crxi'),
        ];

        $this->data['crxx'] = [
            'name' => 'crxx',
            'id' => 'crxx',
            'type' => 'text',
            'value' => $this->form_validation->set_value('crxx'),
        ];

        if ($op == 'edit') {
            if (empty($id)) {
                show_error('cannot update - record not specified', 500);
                return;
            }

            $role = $this->ion_auth->get_role_detail($id);

            //if details could not be retrived from the servera about role, don't return edit form.
            if ($role !== false) {
                $this->data['title'] = 'Update Role';
                $this->data['operation'] = 'Edit';
                $this->data['id']['enable'] = 'enable';
                $this->data['id']['value'] = $this->form_validation->set_value('name', $role['id']);
                $this->data['description']['value'] = $this->form_validation->set_value(
                    'description',
                    array_key_exists('description', $role) ? $role['description'] : '',
                );

                //classes
                $this->data['crxi']['value'] = $this->form_validation->set_value(
                    'crxi',
                    array_key_exists('includeContext', $role) ? $role['includeContext'] : '',
                );
                $this->data['crxx']['value'] = $this->form_validation->set_value(
                    'crxx',
                    array_key_exists('excludeContext', $role) ? $role['excludeContext'] : '',
                );

                // bundles
                $this->data['brxi']['value'] = $this->form_validation->set_value(
                    'brxi',
                    array_key_exists('includeBundles', $role) ? $role['includeBundles'] : '',
                );
                $this->data['brxx']['value'] = $this->form_validation->set_value(
                    'brxx',
                    array_key_exists('excludeBundles', $role) ? $role['excludeBundles'] : '',
                );
            } else {
                $this->data['message'] = $this->ion_auth->errors();

                return;
            }
        }

        $this->data['op'] = $op;

        $this->load->view('auth/add_edit_role', $this->data);
    }

    /**
     * @param $id
     *
     * @rbacName Delete user
     * @rbacGroup User/Role management
     * @rbacAlias auth.delete_user
     */
    public function delete_user($id)
    {
        if (!$this->isDeleteMethod()) {
            respond_not_acceptable();
            return;
        }

        try {
            $id = urldecode($id);
            $this->ion_auth->delete_user($id); // delete from users cfreports users collection
        } catch (Exception $e) {
            $this->ion_auth->set_error($e->getMessage());
        }

        if (is_ajax()) {
            $page = $this->session->userdata('userListPage');
            $count = $this->session->userdata('usersPerPage');
            $showUsers = $this->session->userdata('userListType');

            $users = $this->ion_auth->get_users_array($page, $count, $showUsers, true);
            $this->data['users'] = $users['data'];
            $this->data['totalUsers'] = $users['meta']['total'];
            $this->data['showUsers'] = $showUsers;
            $this->data['current'] = $page;
            $this->data['number_of_rows'] = $count;

            $this->data['message'] = $this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->ion_auth->messages();
            $this->data['userrole'] = $this->session->userdata('role');
            $this->data['is_admin'] = $this->ion_auth->is_admin();
            $this->load->view('auth/user_list', $this->data);
        } else {
            $this->session->set_flashdata('message', $this->ion_auth->messages());
            redirect('auth', 'refresh');
        }
    }

    /**
     * Delete role
     *
     * @rbacName Delete role
     * @rbacGroup User/Role management
     * @rbacAlias auth.delete_role
     *
     * @param type $name - role name
     */
    public function delete_role($id)
    {
        if (!$this->isDeleteMethod()) {
            respond_not_acceptable();
            return;
        }

        if ($id == '') {
            $this->permission_deny($this->lang->line('empty_rolename'));
            return;
        }

        if ($id == 'admin') {
            $this->permission_deny($this->lang->line('admin_role_delete_forbidden'));
            return;
        }

        $this->ion_auth->delete_role($id);

        if (is_ajax()) {
            $this->data['message'] = $this->ion_auth->errors()
                ? $this->ion_auth->errors()
                : $this->ion_auth->messages();
            $this->data['roles'] = $this->ion_auth->get_roles($this->session->userdata('username'));
            $this->data['is_admin'] = $this->ion_auth->is_admin();

            //$this->load->view("auth/list_role", $this->data);
        } else {
            $this->session->set_flashdata('message', $this->ion_auth->messages());
            redirect('auth/manage_role', 'refresh');
        }
    }

    /*
     * if ajax - show message on top of the page div id=error_status
     *
     * if not ajax - redirect to no_permission page, with some message
     */

    public function permission_deny($message = '')
    {
        if ($message == '') {
            $message = $this->lang->line('no_permission');
        }

        if (is_ajax()) {
            respond_unauthorized($message);
        } else {
            $bc = [
                'title' => $this->lang->line('mission_portal_title') . '- access denied',
                'url' => 'welcome/index',
                'isRoot' => true,
            ];

            $this->data['title'] = $this->lang->line('mission_portal_title');
            $this->data['message'] = $this->lang->line('no_permission');
            $this->session->set_flashdata('message', $this->lang->line('no_permission'));

            $this->template->load('templates/singleColumnTemplate', 'auth/no_permission', $this->data);
        }
    }

    /**
     *  Show page with explanation about permission for local admin
     *
     */
    public function no_permission_for_local_admin()
    {
        $this->data['title'] = $this->lang->line('mission_portal_title') . '- access denied';
        $this->data['message'] = $this->lang->line('no_permission');

        $mode = $this->setting_lib->get_authentication_mode();

        switch ($mode) {
            case 'standard':
                $this->data['current_auth_mode'] = 'LDAP';
                break;
            case 'activeDirectory':
                $this->data['current_auth_mode'] = 'Active Directory';
                break;
            default:
                $this->data['current_auth_mode'] = $mode;
        }

        $this->template->load('templates/singleColumnTemplate', 'auth/no_permission_for_local_admin', $this->data);
    }

    private function addRolesData(&$data)
    {
        if (!isActionAllowed('role.list')) {
            redirect('auth/permission_deny', 'refresh');
        }
        $appSettings = $this->settings_rest_model->get_app_settings();
        $data['ldapRolesToSync'] = $appSettings['ldap_roles_list_to_sync'];
        $data['roles'] = array_map(function ($role) {
            return $role['id'];
        }, $this->ion_auth->get_roles());
    }
}
