Documentation
use crate::token::AccessToken;
use crate::token_rsa::AccessTokenRsa;
use crate::{idmlogin::get_idm, rbac::get_rbac};
use crate::{Response, RpConfig};
use actix_http::header::HeaderMap;
use actix_web::{error, Error};
use actix_web::{HttpRequest, HttpResponse};
use anyhow::anyhow;
use dashmap::DashMap;
use regex::Regex;
use serde::Serialize;
use std::sync::OnceLock;
use std::{collections::HashMap, fmt::Debug};

#[derive(Default)]
pub struct PermissionMap {
    pub pmap: HashMap<&'static str, (&'static str, &'static str)>, // 权限检查的列表
    pub whitelist: Vec<&'static str>,                              // 白名单API列表
    pub secret: String,   // 解析token的秘钥, 可以为RSA公钥或HS512的秘钥
    pub use_rsa: bool,    // 是否使用RSA方式进行解密,否则使用秘钥的HS512方式
    pub app_name: String, // 当前业务的名字
    pub use_local: bool,  // 使用本地端的RBAC配置,不优先使用IDM服务器的
    pub config: DashMap<String, String>, // 存放系统相关的一些配置信息,业务自行决定使用
}

static PMAP: OnceLock<PermissionMap> = OnceLock::<PermissionMap>::new();

pub fn init_pmap(
    whitelist: Vec<&'static str>,
    pmap: HashMap<&'static str, (&'static str, &'static str)>,
    secret: String,
    app_name: String,
    use_local: bool,
    use_rsa: bool,
) -> &'static PermissionMap {
    PMAP.get_or_init(|| PermissionMap {
        pmap: pmap,
        whitelist: whitelist,
        secret: secret,
        app_name: app_name,
        use_local: use_local,
        use_rsa: use_rsa,
        config: DashMap::default(),
    })
}

pub fn get_pmap() -> &'static PermissionMap {
    PMAP.get().unwrap()
}

#[derive(thiserror::Error, Debug)]
pub enum VerifyError {
    #[error("Header中无鉴权信息")]
    TokenNone, // 500
    #[error("Header中鉴权信息bearer的格式不对")]
    TokenBearerWrong, // 500
    #[error("rbac_map中缺少`{0}`的RBAC权限配置")]
    RbacNone(String), // 500
    #[error("解析AccessToken鉴权信息失败")]
    TokenDecodeErr, // 401
    #[error("核查所有权限后该用户无对应权限: `{0}`")]
    NoPermission(String), // 403
}

impl PermissionMap {
    pub async fn get_rbac_config(&self) -> RpConfig {
        if self.use_local {
            return get_rbac().rbac.read().await.clone();
        } else {
            let app_rpinfo = if let Some(rpinfo) = get_idm().rpinfo.get(&self.app_name) {
                rpinfo.value().clone()
            } else {
                get_rbac().rbac.read().await.clone()
            };
            app_rpinfo
        }
    }

    pub async fn get_rbac_config_from_map(&self, rbac_key: &String) -> anyhow::Result<RpConfig> {
        if let Some(config) = get_rbac().get_rbac_from_map(rbac_key).await {
            Ok(config)
        } else {
            return Err(anyhow!("无法在RBAC-MAP中找到Key-{}相关的配置", rbac_key));
        }
    }

    pub async fn check_token_only(
        &self,
        req: &HttpRequest,
    ) -> std::result::Result<AccessToken, VerifyError> {
        let (headers, _reqpath) = get_token_and_path(req);
        if !headers.contains_key(actix_http::header::AUTHORIZATION) {
            // return Err(anyhow!("Header中无鉴权信息"));
            return Err(VerifyError::TokenNone);
        }

        let token = headers
            .get(actix_http::header::AUTHORIZATION)
            .unwrap()
            .to_str()
            .unwrap()
            .to_string();
        // if !token.contains("Bearer ") || token.len() < 7 {
        if token.len() < 7 {
            // return Err(anyhow!("Header中鉴权信息bearer的格式不对"));
            return Err(VerifyError::TokenBearerWrong);
        }

        let token = token.split_at(7).1.to_string();
        let access = if self.use_rsa {
            let access = AccessTokenRsa::decode_token(&token, &self.secret);
            if access.is_ok() {
                access.unwrap().to_token()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息失败"));
                return Err(VerifyError::TokenDecodeErr);
            }
        } else {
            let access = AccessToken::decode_token(&token, self.secret.as_str());
            if access.is_ok() {
                access.unwrap()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息失败"));
                return Err(VerifyError::TokenDecodeErr);
            }
        };
        Ok(access)
    }

    pub async fn check_and_verify(
        &self,
        req: &HttpRequest,
    ) -> std::result::Result<AccessToken, VerifyError> {
        let (headers, reqpath) = get_token_and_path(req);
        // tracing::info!("check for {:?}", reqpath);

        if !headers.contains_key(actix_http::header::AUTHORIZATION) {
            // return Err(anyhow!("Header中无鉴权信息"));
            return Err(VerifyError::TokenNone);
        }

        let token = headers
            .get(actix_http::header::AUTHORIZATION)
            .unwrap()
            .to_str()
            .unwrap()
            .to_string();
        // if !token.contains("Bearer ") || token.len() < 7 {
        if token.len() < 7 {
            // return Err(anyhow!("Header中鉴权信息bearer的格式不对"));
            return Err(VerifyError::TokenBearerWrong);
        }

        let token = token.split_at(7).1.to_string();
        let access = if self.use_rsa {
            let access = AccessTokenRsa::decode_token(&token, &self.secret);
            if access.is_ok() {
                access.unwrap().to_token()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息识别"));
                return Err(VerifyError::TokenDecodeErr);
            }
        } else {
            let access = AccessToken::decode_token(&token, self.secret.as_str());
            if access.is_ok() {
                access.unwrap()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息识别"));
                return Err(VerifyError::TokenDecodeErr);
            }
        };

        // 如果是管理员,直接放行
        if access.is_admin {
            return Ok(access);
        }

        // 非正则表达式进行匹配 白名单接口直接放行
        if self.whitelist.contains(&reqpath.as_str()) {
            return Ok(access);
        }
        // 正则表达式进行匹配 白名单接口直接放行
        for each in self.whitelist.iter() {
            if let Ok(re) = Regex::new(*each) {
                if re.captures(&reqpath).is_some() {
                    return Ok(access);
                }
            }
        }

        let user_account = access.user_account.clone();
        let app_rpinfo = self.get_rbac_config().await;

        // 非正则表达式进行匹配
        if let Some((page, item)) = self.pmap.get(reqpath.as_str()) {
            let check_status = app_rpinfo.check_user_action(
                user_account.clone(),
                page.to_string(),
                item.to_string(),
            );
            if check_status.0 {
                // 任意一个角色下,包括default,只要判断了page+item是enabled状态,直接返回成功
                return Ok(access);
            }
        }
        // 正则表达式进行匹配
        for (each_route, (page, item)) in self.pmap.iter() {
            if let Ok(re) = Regex::new(*each_route) {
                if re.captures(&reqpath).is_some() {
                    let check_status = app_rpinfo.check_user_action(
                        user_account.clone(),
                        page.to_string(),
                        item.to_string(),
                    );
                    if check_status.0 {
                        // 任意一个角色下,包括default,只要判断了page+item是enabled状态,直接返回成功
                        return Ok(access);
                    }
                }
            }
        }
        // return Err(anyhow!("核查所有权限后该用户无对应权限:{:?}", user_account));
        return Err(VerifyError::NoPermission(user_account.clone()));
    }

    pub async fn check_and_verify_map(
        &self,
        req: &HttpRequest,
        rbac_key: &String,
    ) -> std::result::Result<AccessToken, VerifyError> {
        let (headers, reqpath) = get_token_and_path(req);
        // tracing::info!("check for {:?}", reqpath);

        if !headers.contains_key(actix_http::header::AUTHORIZATION) {
            // return Err(anyhow!("Header中无鉴权信息"));
            return Err(VerifyError::TokenNone);
        }

        let token = headers
            .get(actix_http::header::AUTHORIZATION)
            .unwrap()
            .to_str()
            .unwrap()
            .to_string();
        // if !token.contains("Bearer ") || token.len() < 7 {
        if token.len() < 7 {
            // return Err(anyhow!("Header中鉴权信息bearer的格式不对"));
            return Err(VerifyError::TokenBearerWrong);
        }

        let token = token.split_at(7).1.to_string();
        let access = if self.use_rsa {
            let access = AccessTokenRsa::decode_token(&token, &self.secret);
            if access.is_ok() {
                access.unwrap().to_token()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息识别"));
                return Err(VerifyError::TokenDecodeErr);
            }
        } else {
            let access = AccessToken::decode_token(&token, self.secret.as_str());
            if access.is_ok() {
                access.unwrap()
            } else {
                // return Err(anyhow!("解析AccessToken鉴权信息识别"));
                return Err(VerifyError::TokenDecodeErr);
            }
        };

        // 如果是管理员,直接放行
        if access.is_admin {
            return Ok(access);
        }

        // 非正则表达式进行匹配 白名单接口直接放行
        if self.whitelist.contains(&reqpath.as_str()) {
            return Ok(access);
        }
        // 正则表达式进行匹配 白名单接口直接放行
        for each in self.whitelist.iter() {
            if let Ok(re) = Regex::new(*each) {
                if re.captures(&reqpath).is_some() {
                    return Ok(access);
                }
            }
        }

        let user_account = access.user_account.clone();
        let app_rpinfo = self.get_rbac_config_from_map(rbac_key).await;
        if app_rpinfo.is_err() {
            return Err(VerifyError::RbacNone(rbac_key.clone()));
        }
        let app_rpinfo = app_rpinfo.unwrap();

        // 非正则表达式进行匹配
        if let Some((page, item)) = self.pmap.get(reqpath.as_str()) {
            let check_status = app_rpinfo.check_user_action(
                user_account.clone(),
                page.to_string(),
                item.to_string(),
            );
            if check_status.0 {
                // 任意一个角色下,包括default,只要判断了page+item是enabled状态,直接返回成功
                return Ok(access);
            }
        }
        // 正则表达式进行匹配
        for (each_route, (page, item)) in self.pmap.iter() {
            if let Ok(re) = Regex::new(*each_route) {
                if re.captures(&reqpath).is_some() {
                    let check_status = app_rpinfo.check_user_action(
                        user_account.clone(),
                        page.to_string(),
                        item.to_string(),
                    );
                    if check_status.0 {
                        // 任意一个角色下,包括default,只要判断了page+item是enabled状态,直接返回成功
                        return Ok(access);
                    }
                }
            }
        }
        // return Err(anyhow!("核查所有权限后该用户无对应权限:{:?}", user_account));
        return Err(VerifyError::NoPermission(user_account.clone()));
    }
}

pub fn get_token_and_path(req: &HttpRequest) -> (HeaderMap, String) {
    let headers = req.headers();
    let reqpath = format!("{} {}", req.method(), req.path());
    (headers.clone(), reqpath)
}

/// 仅仅核验Token是否有效,不做RBAC检查
pub async fn check_token_only<T: Serialize + Debug>(
    req: &HttpRequest,
) -> Result<AccessToken, Error> {
    let access = get_pmap().check_token_only(&req).await.map_err(|e| {
        tracing::error!("token check error: {:?}", e);
        let rsp = get_err_resp::<T>(&e);
        error::InternalError::from_response("", rsp)
    })?;
    Ok(access)
}

/// 同时核验Token是否有效及RBAC权限检查, RBAC配置从默认权限项中获取
pub async fn check_and_verify<T: Serialize + Debug>(
    req: &HttpRequest,
) -> Result<AccessToken, Error> {
    let access = get_pmap().check_and_verify(&req).await.map_err(|e| {
        tracing::error!("token permission error: {:?}", e);
        let rsp = get_err_resp::<T>(&e);
        error::InternalError::from_response("", rsp)
    })?;
    Ok(access)
}

/// 同时核验Token是否有效及RBAC权限检查, RBAC配置从权限字典中根据rbac_key进行获取
pub async fn check_and_verify_map<T: Serialize + Debug>(
    req: &HttpRequest,
    rbac_key: &String,
) -> Result<AccessToken, Error> {
    let access = get_pmap()
        .check_and_verify_map(&req, rbac_key)
        .await
        .map_err(|e| {
            tracing::error!("token permission error: {:?}", e);
            let rsp = get_err_resp::<T>(&e);
            error::InternalError::from_response("", rsp)
        })?;
    Ok(access)
}

pub fn create_error<T: Serialize + Debug>(e: anyhow::Error, err: &str) -> Error {
    tracing::error!("error of {}:{:?}", err, e);
    let rsp = Response::<T>::internal_error(format!("{}:{:?}", err, e).as_str()).finished();
    error::InternalError::from_response("", rsp).into()
}

fn get_err_resp<T: Serialize + Debug>(err: &VerifyError) -> HttpResponse {
    match err {
        VerifyError::TokenNone => {
            Response::<T>::internal_error(format!("Token缺少字段: {:?}", err).as_str()).finished()
        }
        VerifyError::TokenBearerWrong => {
            Response::<T>::internal_error(format!("Token格式错误: {:?}", err).as_str()).finished()
        }
        VerifyError::RbacNone(_) => {
            Response::<T>::internal_error(format!("Token权限为空: {:?}", err).as_str()).finished()
        }
        VerifyError::TokenDecodeErr => {
            Response::<T>::token_expired(format!("Token解析错误: {:?}", err).as_str()).finished()
        }
        VerifyError::NoPermission(_) => {
            Response::<T>::forbidden(format!("Token权限不足: {:?}", err).as_str()).finished()
        }
    }
}