import base64 import hashlib import hmac import os import time from pydantic import BaseModel, Field from configs import dify_config class SignedUrlParams(BaseModel): sign_key: str = Field(..., description="The sign key") timestamp: str = Field(..., description="Timestamp") nonce: str = Field(..., description="Nonce") sign: str = Field(..., description="Signature") class UrlSigner: @classmethod def get_signed_url(cls, url: str, sign_key: str, prefix: str) -> str: signed_url_params = cls.get_signed_url_params(sign_key, prefix) return ( f"{url}?timestamp={signed_url_params.timestamp}" f"&nonce={signed_url_params.nonce}&sign={signed_url_params.sign}" ) @classmethod def get_signed_url_params(cls, sign_key: str, prefix: str) -> SignedUrlParams: timestamp = str(int(time.time())) nonce = os.urandom(16).hex() sign = cls._sign(sign_key, timestamp, nonce, prefix) return SignedUrlParams(sign_key=sign_key, timestamp=timestamp, nonce=nonce, sign=sign) @classmethod def verify(cls, sign_key: str, timestamp: str, nonce: str, sign: str, prefix: str) -> bool: recalculated_sign = cls._sign(sign_key, timestamp, nonce, prefix) return sign == recalculated_sign @classmethod def _sign(cls, sign_key: str, timestamp: str, nonce: str, prefix: str) -> str: if not dify_config.SECRET_KEY: raise Exception("SECRET_KEY is not set") data_to_sign = f"{prefix}|{sign_key}|{timestamp}|{nonce}" secret_key = dify_config.SECRET_KEY.encode() sign = hmac.new(secret_key, data_to_sign.encode(), hashlib.sha256).digest() encoded_sign = base64.urlsafe_b64encode(sign).decode() return encoded_sign