use crate::error::Result;
use crate::license::{LicenseData, SignedLicense};
use crate::verifier::LicenseVerifier;
use std::ops::Deref;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct ValidatedLicense {
inner: SignedLicense,
validated_at: chrono::DateTime<chrono::Utc>,
}
impl ValidatedLicense {
fn new(license: SignedLicense) -> Self {
Self {
inner: license,
validated_at: chrono::Utc::now(),
}
}
pub fn data(&self) -> &LicenseData {
&self.inner.data
}
pub fn validated_at(&self) -> chrono::DateTime<chrono::Utc> {
self.validated_at
}
pub fn has_feature(&self, feature: &str) -> bool {
self.inner.data.has_feature(feature)
}
pub fn days_remaining(&self) -> i64 {
self.inner.data.days_remaining()
}
pub fn into_inner(self) -> SignedLicense {
self.inner
}
}
impl Deref for ValidatedLicense {
type Target = LicenseData;
fn deref(&self) -> &Self::Target {
&self.inner.data
}
}
pub fn require_license(
license_path: impl AsRef<Path>,
public_key_pem: &str,
) -> Result<ValidatedLicense> {
let verifier = LicenseVerifier::from_pem(public_key_pem)?;
let license = verifier.load_and_validate(license_path.as_ref())?;
Ok(ValidatedLicense::new(license))
}
pub fn require_license_with_verifier(
license_path: impl AsRef<Path>,
verifier: &LicenseVerifier,
) -> Result<ValidatedLicense> {
let license = verifier.load_and_validate(license_path.as_ref())?;
Ok(ValidatedLicense::new(license))
}
pub fn validate_license_bytes(
license_bytes: &[u8],
public_key_pem: &str,
) -> Result<ValidatedLicense> {
let verifier = LicenseVerifier::from_pem(public_key_pem)?;
let license = verifier.parse_license(license_bytes)?;
verifier.validate(&license)?;
Ok(ValidatedLicense::new(license))
}
#[macro_export]
macro_rules! load_license {
($license_path:expr) => {{
const PUBLIC_KEY: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/public.pem"));
$crate::require_license($license_path, PUBLIC_KEY)
}};
($license_path:expr, $key_path:expr) => {{
const PUBLIC_KEY: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $key_path));
$crate::require_license($license_path, PUBLIC_KEY)
}};
}
#[macro_export]
#[deprecated(
since = "0.2.0",
note = "Use load_license! macro or licenz-policy crate for enforcement"
)]
macro_rules! require_valid_license {
($license_path:expr) => {{
const PUBLIC_KEY: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/public.pem"));
match $crate::require_license($license_path, PUBLIC_KEY) {
Ok(license) => license,
Err(e) => {
eprintln!("License validation failed: {}", e);
eprintln!("Please ensure you have a valid license file.");
std::process::exit(1);
}
}
}};
($license_path:expr, $key_path:expr) => {{
const PUBLIC_KEY: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $key_path));
match $crate::require_license($license_path, PUBLIC_KEY) {
Ok(license) => license,
Err(e) => {
eprintln!("License validation failed: {}", e);
eprintln!("Please ensure you have a valid license file.");
std::process::exit(1);
}
}
}};
}
#[macro_export]
macro_rules! feature_gate {
($license:expr, $feature:expr, $enabled:block) => {
if $license.has_feature($feature) {
$enabled
}
};
($license:expr, $feature:expr, $enabled:block else $disabled:block) => {
if $license.has_feature($feature) {
$enabled
} else {
$disabled
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{KeyPair, KeySize, LicenseData, LicenseGenerator};
fn create_test_license() -> (String, Vec<u8>) {
let keypair = KeyPair::generate(KeySize::Bits2048).unwrap();
let generator = LicenseGenerator::new(keypair.private_key().clone());
let data = LicenseData::builder()
.id("TEST-001")
.serial("SN-12345")
.customer_id("Test Customer")
.product_id("TestApp")
.valid_days(365)
.feature("basic")
.feature("premium")
.build()
.unwrap();
let signed = generator.generate(data).unwrap();
let binary = generator.export_binary(&signed).unwrap();
let public_key = keypair.export_public_pem().unwrap();
(public_key, binary)
}
#[test]
fn test_validated_license_access() {
let (public_key, binary) = create_test_license();
let license = validate_license_bytes(&binary, &public_key).unwrap();
assert_eq!(license.customer_id, "Test Customer");
assert!(license.has_feature("basic"));
assert!(license.has_feature("premium"));
assert!(!license.has_feature("enterprise"));
}
#[test]
fn test_validated_license_deref() {
let (public_key, binary) = create_test_license();
let license = validate_license_bytes(&binary, &public_key).unwrap();
assert_eq!(license.product_id, "TestApp");
assert_eq!(license.serial, "SN-12345");
}
}