-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
--
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.


local log = require 'mc.logging'
local mdb_service = require 'mc.mdb.mdb_service'

local ACCOUNT_PATH = '/bmc/kepler/AccountService/Accounts/'
local ACCOUNT_INTF = 'bmc.kepler.AccountService.ManagerAccount'

local M = {}

local ROLE_ID_TO_NAME = {
    ['0'] = 'NoAccess',
    ['2'] = 'CommonUser',
    ['3'] = 'Operator',
    ['4'] = 'Administrator',
    ['5'] = 'CustomRole1',
    ['6'] = 'CustomRole2',
    ['7'] = 'CustomRole3',
    ['8'] = 'CustomRole4',
    ['9']  = 'CustomRole5',
    ['10'] = 'CustomRole6',
    ['11'] = 'CustomRole7',
    ['12'] = 'CustomRole8',
    ['13'] = 'CustomRole9',
    ['14'] = 'CustomRole10',
    ['15'] = 'CustomRole11',
    ['16'] = 'CustomRole12',
    ['17'] = 'CustomRole13',
    ['18'] = 'CustomRole14',
    ['19'] = 'CustomRole15',
    ['20'] = 'CustomRole16'
}

local ROLE_NAME_TO_ID = {
    ['noaccess'] = '0',
    ['commonuser'] = '2',
    ['operator'] = '3',
    ['administrator'] = '4',
    ['customrole1'] = '5',
    ['customrole2'] = '6',
    ['customrole3'] = '7',
    ['customrole4'] = '8',
    ['customrole5'] = '9',
    ['customrole6'] = '10',
    ['customrole7'] = '11',
    ['customrole8'] = '12',
    ['customrole9'] = '13',
    ['customrole10'] = '14',
    ['customrole11'] = '15',
    ['customrole12'] = '16',
    ['customrole13'] = '17',
    ['customrole14'] = '18',
    ['customrole15'] = '19',
    ['customrole16'] = '20'
}

local PRIV_NAME_TO_ID = {
    ['UserMgmt'] = 0,
    ['BasicSetting'] = 1,
    ['KVMMgmt'] = 2,
    ['VMMMgmt'] = 3,
    ['SecurityMgmt'] = 4,
    ['PowerMgmt'] = 5,
    ['DiagnoseMgmt'] = 6,
    ['ReadOnly'] = 7,
    ['ConfigureSelf'] = 8
}

local REDFISH_ROLE_MAP = {
    ['ReadOnly'] = 'Login',
    ['UserMgmt'] = 'ConfigureUsers',
    ['BasicSetting'] = 'ConfigureComponents',
    ['ConfigureSelf'] = 'ConfigureSelf'
}

local OEM_ROLE_MAP = {
    ['KVMMgmt'] = 'OemKvm',
    ['VMMMgmt'] = 'OemVmm',
    ['SecurityMgmt'] = 'OemSecurityMgmt',
    ['PowerMgmt'] = 'OemPowerControl',
    ['DiagnoseMgmt'] = 'OemDiagnosis'
}

local BMC_ROLE_MAP = {
   ['Login'] = 'ReadOnly',
   ['ConfigureUsers'] = 'UserMgmt',
   ['ConfigureComponents'] = 'BasicSetting',
   ['ConfigureSelf'] = 'ConfigureSelf',
   ['OemKvm'] = 'KVMMgmt',
   ['OemVmm'] = 'VMMMgmt',
   ['OemSecurityMgmt'] = 'SecurityMgmt',
   ['OemPowerControl'] = 'PowerMgmt',
   ['OemDiagnosis'] = 'DiagnoseMgmt'
}

local function has_usermgmt_priv(privs)
    for _, v in ipairs(privs) do
        if v == 'UserMgmt' then
            return true
        end
    end

    return false
end

function M.get_role_members(Context)
    local privs = Context.Privilege
    local login_user_role = { ROLE_ID_TO_NAME[tostring(Context.RoleId)] }

    local roles = {}
    local path = '/bmc/kepler/AccountService/Roles'
    local interface = 'bmc.kepler.AccountService.Role'
    local ok, objs = pcall(function ()
        return mdb.get_sub_objects(bus, path, interface)
    end)
    if not ok then
        log:error('get role objects failed')
        return roles
    end

    local is_get_all = has_usermgmt_priv(privs)
    for _, v in pairs(objs) do
        local role_path = v['path']
        local _, i = role_path:find(path .. '/')
        local role = ROLE_ID_TO_NAME[role_path:sub(i + 1)]
        if is_get_all then
            roles[#roles + 1] = '/redfish/v1/AccountService/Roles/' .. role
            goto continue
        end
        for _, user_role in ipairs(login_user_role) do
            if user_role == role then
                roles[#roles + 1] = '/redfish/v1/AccountService/Roles/' .. role
            end
        end
        ::continue::
    end

    return roles
end

function M.get_role_member_id(member_id)
    local role_id = ROLE_NAME_TO_ID[string.lower(member_id)]
    if not role_id then
        return
    end
    return ROLE_ID_TO_NAME[role_id]
end

function M.get_role_id(role_name)
    return ROLE_NAME_TO_ID[string.lower(role_name)]
end

function M.is_predefined_role(member_id)
    local role_id = tonumber(ROLE_NAME_TO_ID[string.lower(member_id)])
    return (role_id <= 4) -- 小于等于4的为预制角色
end

local function get_privilege_info(member_id, role_map)
    local role_id = ROLE_NAME_TO_ID[string.lower(member_id)]
    local obj = mdb.get_object(bus, '/bmc/kepler/AccountService/Roles/' .. role_id, 'bmc.kepler.AccountService.Role')
    if not obj then
        return
    end

    local ret = {}
    for _, v in ipairs(obj['RolePrivilege']) do
        if role_map[v] then
            ret[#ret + 1] = role_map[v]
        end
    end
    return ret
end

function M.get_assigned_privileges(member_id)
    return get_privilege_info(member_id, REDFISH_ROLE_MAP)
end

function M.get_oem_privileges(member_id)
    return get_privilege_info(member_id, OEM_ROLE_MAP)
end

function M.check_role_exist(role_name)
    local role_id = ROLE_NAME_TO_ID[string.lower(role_name)]
    if not role_id then
        return false
    end
    local ok, rsp = pcall(mdb_service.is_valid_path, bus, '/bmc/kepler/AccountService/Roles/' .. role_id)
    if not ok or not rsp.Result then
        return false
    end
    return true
end

function M.get_bmc_privileges(privs, is_assigned)
    local bmc_privs = cjson.json_object_new_array()
    if privs == nil then
        if is_assigned then
            bmc_privs = {"ReadOnly", "ConfigureSelf"}
        end
        return bmc_privs
    end
    for _, v in pairs(privs) do
        bmc_privs[#bmc_privs + 1] = BMC_ROLE_MAP[v]
    end
    return bmc_privs
end

-- redfish定义权限
function M.enable_assigned_privileges(privs_tab, priv)
    local assigned_setting = {PRIV_NAME_TO_ID[priv], false}
    for _, v in ipairs(privs_tab) do
        if v == REDFISH_ROLE_MAP[priv] then
            assigned_setting[2] = true
            return assigned_setting
        end
    end
    return assigned_setting
end

-- 服务器定义权限
function M.enable_oem_privileges(privs_tab, priv)
    local oem_setting = {PRIV_NAME_TO_ID[priv], false}
    for _, v in ipairs(privs_tab) do
        if v == OEM_ROLE_MAP[priv] then
            oem_setting[2] = true
            return oem_setting
        end
    end
    return oem_setting
end

return M

