feat(server):增加下发许可证功能。

This commit is contained in:
徐涛 2024-04-03 15:58:53 +08:00
parent 2871ce94d0
commit 854d128b51
6 changed files with 160 additions and 2 deletions

View File

@ -34,3 +34,11 @@ pub async fn load_certificates(certificate_filename: &str) -> anyhow::Result<()>
}
Ok(())
}
pub fn get_public_key() -> &'static X509 {
LICENSE_PUBKEY.get().unwrap()
}
pub fn get_private_key() -> &'static PKey<Private> {
LICENSE_PRIKEY.get().unwrap()
}

View File

@ -0,0 +1,58 @@
use axum::{http::StatusCode, response::IntoResponse, routing, Json, Router};
use base64::{engine::general_purpose::STANDARD, Engine as _};
use openssl::{hash::MessageDigest, rsa::Padding, sign::Signer};
use serde_json::json;
use crate::vo::{self, LicenseRequestForm};
pub struct LicenseController {
routes: Router,
}
impl Into<Router> for LicenseController {
fn into(self) -> Router {
self.routes
}
}
impl LicenseController {
pub fn init() -> Self {
let routes = Router::new().route("/license", routing::post(issue_license));
Self { routes }
}
}
async fn issue_license(Json(request_form): Json<LicenseRequestForm>) -> impl IntoResponse {
let mut license = vo::License::new(
request_form.licensee_name,
request_form.assignee_name,
request_form.assignee_email,
);
for p in request_form.request_products.into_iter() {
license.add_product(p, request_form.valid_days);
}
let serialized_license = license.serialize();
let private_key = crate::certificate::get_private_key();
let mut signer = Signer::new(MessageDigest::sha1(), private_key).unwrap();
signer.set_rsa_padding(Padding::PKCS1).unwrap();
signer.update(serialized_license.as_bytes()).unwrap();
let cert = crate::certificate::get_public_key()
.public_key()
.unwrap()
.public_key_to_der()
.unwrap();
let base64_license = STANDARD.encode(serialized_license);
let base64_signature = STANDARD.encode(signer.sign_to_vec().unwrap());
let base64_cert = STANDARD.encode(cert);
let license_response = format!(
"{}-{}-{}-{}",
license.license_id, base64_license, base64_signature, base64_cert
);
(StatusCode::OK, Json(json!({"license": license_response})))
}

View File

@ -5,7 +5,10 @@ use axum::Router;
/// 生成可迭代的转化为路由定义的控制器列表。
pub fn controllers() -> Box<dyn Iterator<Item = Box<Router>>> {
let controllers: Vec<Box<Router>> = vec![Box::new(products::ProductsController::init().into())];
let controllers: Vec<Box<Router>> = vec![
Box::new(products::ProductsController::init().into()),
Box::new(license::LicenseController::init().into()),
];
Box::from(controllers.into_iter())
}

View File

@ -11,7 +11,7 @@ pub fn empty_to_none<S: Into<String>>(s: S) -> Option<String> {
const RAND_LINCENSE_STR_SRC: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
pub fn generate_product_license_id() -> String {
pub fn generate_license_id() -> String {
let choices = RAND_LINCENSE_STR_SRC.as_bytes();
let mut rng = thread_rng();
let mut code: Vec<String> = vec![];

View File

@ -0,0 +1,87 @@
use chrono::{Duration, Utc};
use serde::{Deserialize, Serialize};
use crate::utils;
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Product {
pub code: String,
pub fallback_date: String,
pub paid_up_to: String,
pub extended: bool,
}
impl Product {
pub fn new<S: AsRef<str>>(code: S, authorize_days: i64) -> Self {
let today = Utc::now().date_naive();
let expires_date = (today + Duration::days(authorize_days))
.format("%Y-%m-%d")
.to_string();
Self {
code: code.as_ref().to_string(),
fallback_date: expires_date.clone(),
paid_up_to: expires_date,
extended: true,
}
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct License {
pub license_id: String,
pub licensee_name: String,
pub assignee_name: String,
pub assignee_email: String,
pub license_restriction: String,
pub check_concurrent_use: bool,
pub products: Vec<Product>,
#[serde(rename = "metadata")]
pub meta_data: String,
pub hash: String,
pub grace_period_days: i32,
pub auto_prolongated: bool,
pub is_auto_prolongated: bool,
}
impl License {
pub fn new(
licensee_name: String,
assignee_name: String,
assignee_email: Option<String>,
) -> Self {
Self {
license_id: utils::generate_license_id(),
licensee_name,
assignee_name,
assignee_email: assignee_email.unwrap_or(String::new()),
license_restriction: String::new(),
check_concurrent_use: false,
products: vec![],
meta_data: String::from("0120230102PPAA013009"),
hash: String::from("41472961/0:1563609451"),
grace_period_days: 7,
auto_prolongated: true,
is_auto_prolongated: true,
}
}
pub fn add_product<S: AsRef<str>>(&mut self, product_code: S, valid_days: i64) {
self.products.push(Product::new(product_code, valid_days));
}
pub fn serialize(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LicenseRequestForm {
pub licensee_name: String,
pub assignee_name: String,
pub assignee_email: Option<String>,
pub valid_days: i64,
pub request_products: Vec<String>,
}

View File

@ -1,3 +1,5 @@
mod license;
mod shared;
pub use license::*;
pub use shared::*;