-- Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
--
-- this file licensed under the 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 singleton = require 'mc.singleton'
local class = require 'mc.class'
local log = require 'mc.logging'
local custom_msg = require 'messages.custom'
local iam_client = require 'iam.client'
local cert_service_enum = require 'iam.json_types.CertificateService'
local signal = require 'mc.signal'

-- 双因素认证配置
local CertificateAuthentication = class()

function CertificateAuthentication:ctor(db)
    self.mutual_auth_state_changed = signal.new()
    self.m_db_config = db:select(db.CertificateAuthentication):first()
end

function CertificateAuthentication:set_certificate_authentication_state(ctx, Enabled)
    if Enabled then
        -- 开启双因素之前判断是否有客户端证书有签发者
        if not self:_check_is_exist_client_cert_has_issuer() then
            log:error("no available user, can't enable mutual authentication.")
            error(custom_msg.FailedEnableTwoFactorCertification())
        end
    end
    self.m_db_config.Enabled = Enabled
    self.m_db_config:save()
    -- 更改ca证书状态
    local ca_enabled = self.m_db_config.InterChassisAuthEnabled or self.m_db_config.Enabled
    self:_modify_ca_privilege(ca_enabled)
    -- 双因素使能变动，踢出所有WEB会话
    self.mutual_auth_state_changed:emit(ctx)
end

function CertificateAuthentication:get_certificate_authentication_state()
    return self.m_db_config.Enabled
end

function CertificateAuthentication:set_ocsp_check_status(ctx, OCSPEnabled)
    self.m_db_config.OCSPEnabled = OCSPEnabled
    self.m_db_config:save()
    -- 在双因素开启后，开启OCSP需要踢出所有WEB会话
    if self.m_db_config.Enabled and OCSPEnabled then
        self.mutual_auth_state_changed:emit(ctx)
    end
end

function CertificateAuthentication:get_ocsp_check_status()
    return self.m_db_config.OCSPEnabled
end

---@function 检查是否有客户端证书有签发者且
---@return boolean
function CertificateAuthentication:_check_is_exist_client_cert_has_issuer()
    for _, obj in pairs(iam_client:GetAccountObjects()) do
        if obj.RootCertUploadedState then
            return true
        end
    end
    return false
end

---@function 更新ca的删除权限
---@param value integer true代表可以删除，false代表不可以删除
function CertificateAuthentication:_modify_ca_privilege(value)
    if not value then
        iam_client:ForeachCAObjects(function(obj)
            obj.Privilege = obj.Privilege | 1
        end
        )
    else
        iam_client:ForeachCAObjects(function(obj)
            obj.Privilege = obj.Privilege & 0xFFFFFFFE
        end
        )
    end
end

local function check_if_ca_certificate_exists()
    local cnt = 0
    for _, obj in pairs(iam_client:GetCertificateObjects()) do
        if obj.CertificateUsageType == cert_service_enum.CertificateUsageType.ManagerCACertificate:value() then
            cnt = cnt + 1
        end
    end
    return (cnt > 0)
end

function CertificateAuthentication:set_inter_chassis_auth_enabled(enabled)
    -- 开启板间通信使能前需要确保有CA证书
    if enabled and not check_if_ca_certificate_exists() then
        log:error("no available ca certificate, can't enable inter-chassis authentication.")
        error(custom_msg.RootCertificateNotImported())
    end
    self.m_db_config.InterChassisAuthEnabled = enabled
    self.m_db_config:save()
    -- 更改ca证书状态
    local ca_enabled = self.m_db_config.InterChassisAuthEnabled or self.m_db_config.Enabled
    self:_modify_ca_privilege(ca_enabled)
end

function CertificateAuthentication:get_inter_chassis_auth_enabled()
    return self.m_db_config.InterChassisAuthEnabled
end

local validation_map = {
    ['None'] = true,
    ['LLDP'] = true
}

function CertificateAuthentication:set_inter_chassis_validation(value)
    if not validation_map[value] then
        error(custom_msg.PropertyValueNotInList(value, "InterChassisValidation"))
    end
    self.m_db_config.InterChassisValidation = value
    self.m_db_config:save()
end

function CertificateAuthentication:get_inter_chassis_validation()
    return self.m_db_config.InterChassisValidation
end

return singleton(CertificateAuthentication)