first commit

This commit is contained in:
ny0c 2022-12-22 18:54:55 +08:00
commit b10773b7dd
13 changed files with 3357 additions and 0 deletions

1919
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

50
Cargo.toml Normal file
View File

@ -0,0 +1,50 @@
[package]
name = "nv_ls"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rsa = "0.7.2"
ring = "0.16.20"
rand = "0.8.5"
pem = { version = "1.0" }
rcgen = { version = "0.10.0", features = ["pem"] }
num-traits = { version = "0.2", default-features = false }
num-bigint-dig = { version = "0.8", default-features = false }
sha2 = "0.10.6"
base64 = "0.13.1"
uuid = { version = "1.2.2", features = ["v4", "fast-rng", "macro-diagnostics", "serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
jsonwebtoken = "8"
time = { version = "0.3.17", features = ["macros", "serde", "formatting", "parsing"] }
tokio = { version = "1.13.1", features = ["full"] }
actix-web = { version = "4", features = ["rustls"] }
actix-web-httpauth = "0.8.0"
rustls = "0.20"
rustls-pemfile = "1"
env_logger = "0.10"
log = "0.4"
futures = "0.3.25"
redis = { version = "0.22", features = ["tokio-comp"] }
obfstr = "0.4.1"
derive_more = "0.99.17"
[profile.dev.package.num-bigint-dig]
opt-level = 3
[profile.release]
strip = true
opt-level = 3
lto = true
codegen-units = 1
panic = "abort"

73
readme.md Normal file
View File

@ -0,0 +1,73 @@
## config.json
### server_addr
The service listener addr.
### server_port
The service listener port.
### domain
Automatic generation of ssl certificates for domain names.
If you have a certificate, you do not need to change anything.
### req_host
The host filled in the client's token, it is also the address of the client request
### req_port
The port filled in the client's token, it is also the port of the client request
### redis_url
Same as the name
### redis_task_interval
How often to automatically clean up the client release inside redis.
### scope_ref_list
Whatever, just let it as is, or ur can random some uuid v4, but keep in mind it only takes two
### nls_service_instance_ref
Whatever, just let it as is, or ur can random a new uuid v4
### lease_time
Client lease time, In second
### lease_renewal_factor
The interval factor between client vm requests to the server, expressed as a percentage.
*lease_time* * *lease_renewal_factor*
e.g:
lease_time: 600s, lease_renewal_factor: 20, time now: 2022/12/03 10:00:00
It means, The time of the next client request time is: 2022/12/03 10:02:00, and next time is 2022/12/03 10:04:00 till the client lease renew
### cert_https
Same as the name
### rsa_client_token
For vm client token encryption and vm-side verification of signatures, can delete the corresponding file and generate it again randomly
## Get vm client token
just access `https://ur-ip-addr:server_port/genClientToken`

440
src/api.rs Normal file
View File

@ -0,0 +1,440 @@
use actix_web::{get, post, put, delete, web, Result, Responder, HttpResponse, http::header::ContentType, Either};
use std::str::FromStr;
use num_traits::cast::ToPrimitive;
use sha2::{Sha256, Digest};
use uuid::{Uuid, uuid};
use time::OffsetDateTime;
use time::macros::format_description;
use rsa::{PublicKeyParts, RsaPublicKey};
use rsa::pkcs8::{DecodePublicKey, EncodePublicKey, LineEnding};
use crate::core_struct::{AppConfigState, NodeUrl, PortMap, PortSet, JwtAuthToken};
use crate::core_struct::origin::{OriginRequest, OriginResponse};
use crate::core_struct::code::{CodeRequest, CodeResponse, AcData};
use crate::core_struct::client_token::{ClientTokenRequest, ServiceInstanceConfiguration, ServiceInstancePublicKeyConfiguration, ServiceInstancePublicKeyMe};
use crate::core_struct::token::{AuthCode, TokenRequest, TokenResponse};
use crate::core_struct::leases::{ClientLeasesResponse, AddLessorResponse, CreateLeaseResult, LeaseCreateDetail, UpdateLeasesResponse, UpdateLeasesErrorResponse, DeleteLeasesResponse};
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum ApiError {
#[display(fmt = "Api Error: {}", detail)]
InternalError { detail: String },
}
impl actix_web::error::ResponseError for ApiError {
fn status_code(&self) -> actix_web::http::StatusCode {
match *self {
ApiError::InternalError { .. } => actix_web::http::StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code())
.insert_header(ContentType::html())
.body(self.to_string())
}
}
#[get("/genClientToken")]
pub async fn gen_client_token(config_state: web::Data<AppConfigState>) -> HttpResponse {
let req_port = &config_state.req_port;
let req_host = &config_state.req_host;
let token_public_key_str = &config_state.rsa_client_token.public_key.clone();
let token_public_key = RsaPublicKey::from_public_key_pem(std::str::from_utf8(token_public_key_str).expect("error read key")).expect("Error get public key");
let key_modulus = token_public_key.n().clone();
let key_exponent = token_public_key.e().clone();
let current_time = OffsetDateTime::now_utc();
let exp_time = current_time.clone() + std::time::Duration::from_secs(60 * 60 * 24 * 30);
let client_token_obj = ClientTokenRequest {
jti: Uuid::new_v4(),
iss: "NLS Service Instance".to_string(),
aud: "NLS Licensed Client".to_string(),
iat: current_time.clone(),
nbf: current_time.clone(),
exp: exp_time,
update_mode: "ABSOLUTE".to_string(),
scope_ref_list: config_state.scope_ref_list.clone(),
fulfillment_class_ref_list: None,
service_instance_configuration: ServiceInstanceConfiguration {
nls_service_instance_ref: config_state.nls_service_instance_ref.clone(),
svc_port_set_list: vec![PortSet {
idx: 0,
d_name: "DLS".to_string(),
svc_port_map: vec![
PortMap {
service: "auth".to_string(),
port: req_port.clone(),
}, PortMap {
service: "lease".to_string(),
port: req_port.clone(),
},
],
}],
node_url_list: vec![
NodeUrl {
idx: 0,
url: req_host.clone(),
url_qr: req_host.clone(),
svc_port_set_idx: 0,
}
],
},
service_instance_public_key_configuration: ServiceInstancePublicKeyConfiguration {
service_instance_public_key_me: ServiceInstancePublicKeyMe { type_mod: format!("{:x}", key_modulus), exp: key_exponent.to_u32().unwrap() },
service_instance_public_key_pem: token_public_key.to_public_key_pem(LineEnding::LF).unwrap(),
key_retention_mode: "LATEST_ONLY".to_string(),
},
};
let jwt_header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256);
let client_token_str = jsonwebtoken::encode(
&jwt_header,
&client_token_obj,
&jsonwebtoken::EncodingKey::from_rsa_pem(&config_state.rsa_client_token.private_key).expect("Error read jwt encode key!"),
).expect("Failed to encode jwt data!");
let tok_format = format_description!("[day]-[month]-[year]-[hour]:[minute]:[second]");
let tok_time = OffsetDateTime::now_utc().format(&tok_format).unwrap();
HttpResponse::Ok()
.content_type(ContentType::octet_stream())
.append_header(("Content-Disposition", format!("attachment; filename=\"client_configuration_token_{}.tok\"", &tok_time)))
.body(client_token_str.into_bytes())
}
#[post("/auth/v1/origin")]
pub async fn origin_req(origin_request: web::Json<OriginRequest>) -> Result<impl Responder> {
let origin_response = OriginResponse {
origin_ref: origin_request.candidate_origin_ref,
environment: origin_request.environment.clone(),
svc_port_set_list: None,
node_url_list: None,
node_query_order: None,
prompts: None,
sync_timestamp: OffsetDateTime::now_utc(),
};
Ok(web::Json(origin_response))
}
// todo error catch
#[post("/auth/v1/code")]
pub async fn code_req(code_request: web::Json<CodeRequest>, config_state: web::Data<AppConfigState>) -> Either<web::Json<CodeResponse>, Result<HttpResponse, ApiError>> {
let time_now = OffsetDateTime::now_utc();
let exp_time = time_now.clone() + std::time::Duration::from_secs(600);
let code_challenge = code_request.code_challenge.clone();
let ac_data = AcData {
iat: time_now,
exp: exp_time,
challenge: code_challenge,
origin_ref: code_request.origin_ref,
key_ref: uuid!("00000000-0000-0000-0000-000000000000"),
kid: uuid!("00000000-0000-0000-0000-000000000000"),
};
let mut jwt_header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256);
jwt_header.kid = Option::from("00000000-0000-0000-0000-000000000000".to_string());
let auth_code = jsonwebtoken::encode(
&jwt_header,
&ac_data,
&jsonwebtoken::EncodingKey::from_rsa_pem(&config_state.rsa_server_jwt.private_key).expect("Error read jwt encode key!"),
).expect("Failed to encode jwt data!");
let code_resp = CodeResponse {
auth_code,
sync_timestamp: OffsetDateTime::now_utc(),
prompts: None,
};
Either::Left(web::Json(code_resp))
}
#[post("/auth/v1/token")]
pub async fn auth_token(token_request: web::Json<TokenRequest>, config_state: web::Data<AppConfigState>) -> Either<web::Json<TokenResponse>, Result<HttpResponse, ApiError>> {
// decode jwt
let client_auth_code = match jsonwebtoken::decode::<AuthCode>(
&token_request.auth_code,
&jsonwebtoken::DecodingKey::from_rsa_pem(&config_state.rsa_server_jwt.public_key).expect("Error read jwt decode key"),
&jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::RS256),
) {
Ok(data) => data,
Err(decode_error) => {
log::info!("jwt decode error!");
return Either::Right(Err(ApiError::InternalError { detail: decode_error.to_string() }));
}
};
// check challenge
let code_verifier = token_request.code_verifier.clone();
let mut hasher = Sha256::new();
hasher.update(&code_verifier);
let result = hasher.finalize();
let b64_result = base64::encode(result).rsplit("=").collect::<String>();
if b64_result != client_auth_code.claims.challenge {
log::info!("Challenge failed!");
return Either::Right(Err(ApiError::InternalError { detail: "Challenge Failed!".to_string() }));
}
let time_now = OffsetDateTime::now_utc();
let exp_time = time_now.clone() + std::time::Duration::from_secs(60 * 60); // 1hr
let auth_token = JwtAuthToken {
iat: time_now.clone(),
nbf: time_now.clone(),
iss: "https://cls.nvidia.org".to_string(),
aud: "https://cls.nvidia.org".to_string(),
exp: exp_time.clone(),
origin_ref: client_auth_code.claims.origin_ref,
key_ref: client_auth_code.claims.key_ref,
kid: client_auth_code.claims.kid,
};
let mut jwt_header = jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256);
jwt_header.kid = Option::from("00000000-0000-0000-0000-000000000000".to_string());
let auth_token = jsonwebtoken::encode(
&jwt_header,
&auth_token,
&jsonwebtoken::EncodingKey::from_rsa_pem(&config_state.rsa_server_jwt.private_key).expect("Error read jwt encode key!"),
).expect("Failed to encode jwt data!");
let token_resp = TokenResponse {
auth_token,
expires: exp_time,
prompts: None,
sync_timestamp: time_now,
};
Either::Left(web::Json(token_resp))
}
// todo error catch
#[get("/v1/lessor/leases")]
pub async fn get_all_leases(jwt_auth_token: Option<web::ReqData<JwtAuthToken>>, redis_client: web::Data<redis::Client>) -> Result<impl Responder> {
let time_now = OffsetDateTime::now_utc();
let mut client_leases = ClientLeasesResponse {
active_lease_list: vec![],
prompts: None,
sync_timestamp: time_now,
};
let origin_ref: Uuid = match jwt_auth_token {
Some(data) => data.origin_ref,
None => {
return Ok(web::Json(client_leases));
}
};
let mut redis_conn = redis_client.get_async_connection().await.expect("failed to get redis connection!");
let lease_data: Vec<String> = redis::cmd("ZRANGEBYSCORE")
.arg(&[
origin_ref.to_string().as_str(),
format!("({}", time_now.unix_timestamp()).as_str(),
"+inf"
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
if lease_data.len() == 0 {
return Ok(web::Json(client_leases));
} else {
for lease in lease_data {
client_leases.active_lease_list.push(Uuid::from_str(lease.as_str()).unwrap());
}
}
Ok(web::Json(client_leases))
}
#[post("/v1/lessor")]
pub async fn add_lessor(
jwt_auth_token: Option<web::ReqData<JwtAuthToken>>,
config_state: web::Data<AppConfigState>,
redis_client: web::Data<redis::Client>,
) -> Either<web::Json<AddLessorResponse>, Result<HttpResponse, actix_web::Error>> {
let origin_ref: Uuid = match jwt_auth_token {
Some(data) => data.origin_ref,
None => {
return Either::Right(Ok(HttpResponse::Ok().content_type("application/json").body("none")));
}
};
let time_now = OffsetDateTime::now_utc();
let exp_time = time_now + time::Duration::minutes(i64::from(config_state.lease_time));
let new_ref = Uuid::new_v4();
let mut redis_conn = redis_client.get_async_connection().await.expect("failed to get redis connection!");
let redis_status: i32 = redis::cmd("ZADD")
.arg(&[
origin_ref.to_string().as_str(),
exp_time.unix_timestamp().to_string().as_str(),
new_ref.to_string().as_str()
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
if !(redis_status == 1 || redis_status == 0) {
return Either::Right(Ok(HttpResponse::Ok().content_type("application/json").body("none")));
}
let add_resp = AddLessorResponse {
lease_result_list: vec![
CreateLeaseResult {
error: None,
lease: LeaseCreateDetail {
type_ref: new_ref,
created: time_now,
expires: exp_time,
recommended_lease_renewal: config_state.lease_renewal_factor.clone(),
offline_lease: false,
license_type: "CONCURRENT_COUNTED_SINGLE".to_string(),
},
ordinal: None,
}
],
prompts: None,
result_code: None,
sync_timestamp: time_now,
};
Either::Left(web::Json(add_resp))
}
#[put("/v1/lease/{lease_ref}")]
pub async fn update_lessor(
req_path: web::Path<Uuid>,
jwt_auth_token: Option<web::ReqData<JwtAuthToken>>,
config_state: web::Data<AppConfigState>,
redis_client: web::Data<redis::Client>,
) -> Either<web::Json<UpdateLeasesResponse>, Result<HttpResponse, actix_web::Error>> {
// env setup
let time_now = OffsetDateTime::now_utc();
let exp_time = time_now + time::Duration::minutes(i64::from(config_state.lease_time));
let lease_ref = req_path.into_inner();
let mut redis_conn = redis_client.get_async_connection().await.expect("failed to get redis connection!");
let mut error_resp = UpdateLeasesErrorResponse {
code: 404,
message: "".to_string(),
prompts: None,
sync_timestamp: time_now,
};
// get ref from jwt
let origin_ref: Uuid = match jwt_auth_token {
Some(data) => data.origin_ref,
None => {
error_resp.code = 500;
error_resp.message = format!("Unable to get origin_ref!");
return Either::Right(Ok(HttpResponse::BadRequest().content_type("application/json").body(serde_json::to_string(&error_resp).unwrap())));
}
};
// get all lease by ref from redis
let lease_data: Vec<String> = redis::cmd("ZRANGEBYSCORE")
.arg(&[
origin_ref.to_string().as_str(),
format!("({}", time_now.unix_timestamp()).as_str(),
"+inf"
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
// check lease_ref in db or not
if !lease_data.contains(&lease_ref.to_string()) {
error_resp.message = format!("no current lease found for: {}", lease_ref);
return Either::Right(Ok(HttpResponse::NotFound().content_type("application/json").body(serde_json::to_string(&error_resp).unwrap())));
} else {
// update the lease
let redis_status: i32 = redis::cmd("ZADD")
.arg(&[
origin_ref.to_string().as_str(),
exp_time.unix_timestamp().to_string().as_str(),
lease_ref.to_string().as_str()
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
if !(redis_status == 1 || redis_status == 0) {
error_resp.code = 500;
error_resp.message = format!("Failed to update lease: {}", lease_ref);
return Either::Right(Ok(HttpResponse::InternalServerError().content_type("application/json").body(serde_json::to_string(&error_resp).unwrap())));
}
}
let update_resp = UpdateLeasesResponse {
expires: exp_time,
lease_ref: lease_ref,
offline_lease: false,
prompts: None,
recommended_lease_renewal: config_state.lease_renewal_factor.clone(),
sync_timestamp: time_now,
};
Either::Left(web::Json(update_resp))
}
#[delete("/v1/lessor/leases")]
pub async fn delete_all_leases(
jwt_auth_token: Option<web::ReqData<JwtAuthToken>>,
redis_client: web::Data<redis::Client>,
) -> Either<web::Json<DeleteLeasesResponse>, Result<HttpResponse, actix_web::Error>> {
// env setup
let time_now = OffsetDateTime::now_utc();
let mut redis_conn = redis_client.get_async_connection().await.expect("failed to get redis connection!");
// get ref from jwt
let origin_ref: Uuid = match jwt_auth_token {
Some(data) => data.origin_ref,
None => {
return Either::Right(Ok(HttpResponse::BadRequest().content_type("text/html").body("None")));
}
};
let mut del_resp = DeleteLeasesResponse {
release_failure_list: None,
released_lease_list: vec![],
prompts: None,
sync_timestamp: time_now,
};
// get all lease by ref from redis
let lease_data: Vec<String> = redis::cmd("ZRANGEBYSCORE")
.arg(&[
origin_ref.to_string().as_str(),
format!("({}", time_now.unix_timestamp()).as_str(),
"+inf"
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
if !lease_data.is_empty() {
// delete the lease by ref
let redis_status: i32 = redis::cmd("DEL")
.arg(&[
origin_ref.to_string().as_str(),
])
.query_async(&mut redis_conn)
.await.expect("unable to get redis");
if redis_status != 1 {
del_resp.release_failure_list = Some(lease_data.iter().map(|x| Uuid::from_str(x.as_str()).unwrap()).collect());
}
for lease in lease_data {
del_resp.released_lease_list.push(Uuid::from_str(lease.as_str()).unwrap());
}
}
Either::Left(web::Json(del_resp))
}

58
src/cert_tools.rs Normal file
View File

@ -0,0 +1,58 @@
#![allow(clippy::complexity, clippy::style, clippy::pedantic)]
use rsa::{RsaPrivateKey, RsaPublicKey};
use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
use rand::rngs::OsRng;
use rcgen::{Certificate, CertificateParams, DistinguishedName, date_time_ymd, DnType};
use std::convert::TryFrom;
use crate::utils::MyRsaKeyPair;
pub fn gen_rsa2048() -> MyRsaKeyPair {
let mut rng = rand::thread_rng();
let bits = 2048;
let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let pub_key = RsaPublicKey::from(&priv_key);
let pub_key_str = pub_key.to_public_key_pem(LineEnding::LF).unwrap();
let priv_key_str = priv_key.to_pkcs8_pem(LineEnding::LF).unwrap().as_str().to_owned();
MyRsaKeyPair {
public_key: pub_key_str.into_bytes(),
private_key: priv_key_str.into_bytes(),
}
}
pub fn gen_cert_rsa2048(common_name: String) -> MyRsaKeyPair {
let mut params: CertificateParams = Default::default();
params.not_before = date_time_ymd(2021, 01, 01);
params.not_after = date_time_ymd(2030, 12, 30);
params.distinguished_name = DistinguishedName::new();
params.distinguished_name.push(DnType::CommonName, common_name);
params.distinguished_name.push(DnType::OrganizationalUnitName, "Server Cert");
params.distinguished_name.push(DnType::OrganizationName, "ORG");
params.distinguished_name.push(DnType::CountryName, "RS");
params.alg = &rcgen::PKCS_RSA_SHA256;
let mut rng = OsRng;
let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to gen private key!");
let private_key_der = private_key.to_pkcs8_der().unwrap();
let key_pair = rcgen::KeyPair::try_from(private_key_der.as_bytes()).unwrap();
params.key_pair = Some(key_pair);
let cert = Certificate::from_params(params).expect("Failed to gen cert!");
let pem_serialized = cert.serialize_pem().unwrap();
let cert_str = pem_serialized;
let cert_key = cert.serialize_private_key_pem();
MyRsaKeyPair {
public_key: cert_str.into_bytes(),
private_key: cert_key.into_bytes(),
}
}

View File

@ -0,0 +1,50 @@
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use time::serde as time_serde;
use crate::core_struct::{NodeUrl, PortSet};
time_serde::format_description!(rfc3339_ms_z, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z");
#[derive(Serialize, Deserialize, Debug)]
pub struct ClientTokenRequest {
pub jti: Uuid,
pub iss: String,
pub aud: String,
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub nbf: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
// TODO should come from UI as param
pub update_mode: String,
pub scope_ref_list: Vec<Uuid>,
pub fulfillment_class_ref_list: Option<Vec<String>>,
pub service_instance_configuration: ServiceInstanceConfiguration,
pub service_instance_public_key_configuration: ServiceInstancePublicKeyConfiguration,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ServiceInstanceConfiguration {
pub nls_service_instance_ref: Uuid,
pub svc_port_set_list: Vec<PortSet>,
pub node_url_list: Vec<NodeUrl>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ServiceInstancePublicKeyConfiguration {
pub service_instance_public_key_me: ServiceInstancePublicKeyMe,
pub service_instance_public_key_pem: String,
pub key_retention_mode: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ServiceInstancePublicKeyMe {
#[serde(rename = "mod")]
pub type_mod: String,
pub exp: u32,
}

37
src/core_struct/code.rs Normal file
View File

@ -0,0 +1,37 @@
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use time::serde as time_serde;
use crate::core_struct::LicenseProviderPrompt;
time_serde::format_description!(rfc3339_ms_z, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z");
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CodeRequest {
pub code_challenge: String,
pub origin_ref: Uuid,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CodeResponse {
// jwt
pub auth_code: String,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AcData {
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
pub challenge: String,
pub origin_ref: Uuid,
pub key_ref: Uuid,
pub kid: Uuid,
}

93
src/core_struct/leases.rs Normal file
View File

@ -0,0 +1,93 @@
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use time::serde as time_serde;
use crate::core_struct::LicenseProviderPrompt;
time_serde::format_description!(rfc3339_ms_z, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z");
time_serde::format_description!(rfc3339_ms, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]");
#[derive(Serialize, Deserialize, Debug)]
pub struct ClientLeasesResponse {
pub active_lease_list: Vec<Uuid>,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ClientLeasesErrorResponse {
pub detail: String,
pub status: u32,
pub title: String,
#[serde(rename = "type")]
pub error_type: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AddLessorResponse {
pub lease_result_list: Vec<CreateLeaseResult>,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
// one of "SUCCESS", "FULFILLMENT_FAILURE", "INVALID_LEASE_PROPOSAL", "UNKNOWN_ACCESS_GROUP", "INFRASTRUCTURE_FAILURE", or None -> SUCCESS
pub result_code: Option<String>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CreateLeaseResult {
pub error: Option<String>,
pub lease: LeaseCreateDetail,
pub ordinal: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LeaseCreateDetail {
#[serde(rename = "ref")]
pub type_ref: Uuid,
#[serde(with = "rfc3339_ms")]
pub created: OffsetDateTime,
#[serde(with = "rfc3339_ms")]
pub expires: OffsetDateTime,
pub recommended_lease_renewal: f32,
pub offline_lease: bool,
// default to CONCURRENT_COUNTED_SINGLE now, seem apply for all req
pub license_type: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct UpdateLeasesResponse {
#[serde(with = "rfc3339_ms_z")]
pub expires: OffsetDateTime,
pub lease_ref: Uuid,
pub offline_lease: bool,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
pub recommended_lease_renewal: f32,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct UpdateLeasesErrorResponse {
pub message: String,
pub code: u16,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DeleteLeasesResponse {
pub release_failure_list: Option<Vec<Uuid>>,
pub released_lease_list: Vec<Uuid>,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}

76
src/core_struct/mod.rs Normal file
View File

@ -0,0 +1,76 @@
pub mod origin;
pub mod code;
pub mod client_token;
pub mod token;
pub mod leases;
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use crate::utils::MyRsaKeyPair;
#[derive(Clone)]
pub struct AppConfigState {
pub req_port: u16,
pub req_host: String,
pub scope_ref_list: Vec<Uuid>,
pub nls_service_instance_ref: Uuid,
pub lease_time: u16,
pub lease_renewal_factor: f32,
pub rsa_client_token: MyRsaKeyPair,
pub rsa_server_jwt: MyRsaKeyPair,
}
#[derive(Clone)]
pub struct RedisTaskConfig {
pub redis_url: String,
pub task_interval: u16,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PortSet {
pub idx: u8,
pub d_name: String,
pub svc_port_map: Vec<PortMap>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PortMap {
pub service: String,
pub port: u16,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct NodeUrl {
pub idx: u8,
pub url: String,
pub url_qr: String,
pub svc_port_set_idx: u8,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LicenseProviderPrompt {
// datetime
pub ts: String,
pub prompt_ref: String,
pub operation_type: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JwtAuthToken {
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub nbf: OffsetDateTime,
pub iss: String,
pub aud: String,
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
pub origin_ref: Uuid,
pub key_ref: Uuid,
pub kid: Uuid,
}

43
src/core_struct/origin.rs Normal file
View File

@ -0,0 +1,43 @@
use uuid::Uuid;
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use time::serde as time_serde;
use crate::core_struct::{LicenseProviderPrompt, NodeUrl, PortSet};
time_serde::format_description!(rfc3339_ms_z, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z");
#[derive(Serialize, Deserialize, Debug)]
pub struct OriginRequest {
pub environment: EnvironmentData,
pub candidate_origin_ref: Uuid,
pub registration_pending: bool,
pub update_pending: bool,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct EnvironmentData {
pub fingerprint: FingerprintData,
pub guest_driver_version: String,
pub hostname: String,
pub os_platform: String,
pub os_version: String,
pub ip_address_list: Vec<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FingerprintData {
pub mac_address_list: Vec<String>,
}
#[derive(Serialize, Debug)]
pub struct OriginResponse {
pub origin_ref: Uuid,
pub environment: EnvironmentData,
pub svc_port_set_list: Option<Vec<PortSet>>,
pub node_url_list: Option<Vec<NodeUrl>>,
pub node_query_order: Option<Vec<u8>>,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}

47
src/core_struct/token.rs Normal file
View File

@ -0,0 +1,47 @@
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use time::serde as time_serde;
use uuid::Uuid;
use crate::core_struct::LicenseProviderPrompt;
time_serde::format_description!(rfc3339_ms_z, OffsetDateTime, "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:6]Z");
#[derive(Serialize, Deserialize, Debug)]
pub struct TokenRequest {
pub auth_code: String,
pub code_verifier: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AuthCode {
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
pub challenge: String,
pub origin_ref: Uuid,
pub key_ref: Uuid,
pub kid: Uuid,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TokenResponse {
pub auth_token: String,
#[serde(with = "rfc3339_ms_z")]
pub expires: OffsetDateTime,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct TokenErrorResponse {
pub code: u16,
pub message: String,
pub prompts: Option<Vec<LicenseProviderPrompt>>,
#[serde(with = "rfc3339_ms_z")]
pub sync_timestamp: OffsetDateTime,
}

220
src/main.rs Normal file
View File

@ -0,0 +1,220 @@
mod utils;
mod cert_tools;
mod core_struct;
mod api;
use std::net::SocketAddr;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use actix_web::{get, web, App, Error, HttpServer, Responder, HttpResponse, middleware, dev::ServiceRequest, HttpMessage};
use actix_web_httpauth::{
extractors::bearer::BearerAuth,
middleware::HttpAuthentication,
};
use rustls::{Certificate, PrivateKey, ServerConfig};
use time::OffsetDateTime;
use crate::utils::{load_config, prepare_redis};
use crate::core_struct::{AppConfigState, JwtAuthToken, RedisTaskConfig};
use crate::api::{add_lessor, auth_token, code_req, delete_all_leases, gen_client_token, get_all_leases, origin_req, update_lessor};
use crate::core_struct::leases::ClientLeasesErrorResponse;
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
// todo impl new type for json error
async fn leases_validator(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, (Error, ServiceRequest)> {
let config_state = req.app_data::<web::Data<AppConfigState>>().cloned().expect("Server Error");
// decode jwt
let client_auth_token = match jsonwebtoken::decode::<JwtAuthToken>(
&credentials.token(),
&jsonwebtoken::DecodingKey::from_rsa_pem(&config_state.rsa_server_jwt.public_key).expect("Error read jwt decode key"),
&jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::RS256),
) {
Ok(token_data) => token_data,
Err(jwt_error) => {
let error_resp = ClientLeasesErrorResponse {
detail: jwt_error.to_string(),
status: 401,
title: "Unauthorized".to_string(),
error_type: "about:blank".to_string(),
};
let new_error = actix_web::error::ErrorUnauthorized(serde_json::to_string_pretty(&error_resp).unwrap());
return Err((new_error, req));
}
};
req.extensions_mut().insert(client_auth_token.claims);
Ok(req)
}
async fn async_clean_lease(task_config: RedisTaskConfig, shutdown_marker: Arc<AtomicBool>) -> std::io::Result<()> {
let job_interval = i64::from(task_config.task_interval) * 60;
let start_time = OffsetDateTime::now_utc();
loop {
if shutdown_marker.load(Ordering::SeqCst) {
log::info!("RedisTask Stop.");
break;
}
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
if ((OffsetDateTime::now_utc() - start_time).whole_seconds() % job_interval) != 0 {
continue
}
let _ = match redis::Client::open(task_config.redis_url.clone().as_str()) {
Ok(redis_client) => {
let _ = match redis_client.get_async_connection().await {
Ok(mut redis_connection) => {
let _ = match redis::cmd("keys").arg(&["*"]).query_async::<_, Vec<String>>(&mut redis_connection).await {
Ok(ref_list) => {
let time_now = OffsetDateTime::now_utc();
for ref_id in ref_list {
let _ = match redis::cmd("ZREMRANGEBYSCORE")
.arg(&[
ref_id.as_str(),
"0",
time_now.unix_timestamp().to_string().as_str()
])
.query_async::<_, i32>(&mut redis_connection)
.await {
Ok(redis_status) => {
if redis_status != 0 {
log::info!("RedisTask: ref: {} clean with {} leases.", &ref_id, &redis_status);
}
}
Err(redis_err) => {
log::debug!("RedisTask: cannot remove ref: {}, Cause: {}", &ref_id, redis_err);
}
};
}
}
Err(redis_err) => {
log::debug!("RedisTask: Cannot get keys! Cause: {}", redis_err);
}
};
}
Err(_) => {
log::debug!("RedisTask: Cannot open redis connection!")
}
};
}
Err(_) => {
log::debug!("RedisTask: Cannot open redis client!")
}
};
log::debug!("RedisTask cleanup done.");
}
Ok(())
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// log setting
env_logger::init_from_env(env_logger::Env::default().default_filter_or("debug"));
// startup check
let config_data = load_config();
let redis_client = web::Data::new(prepare_redis(config_data.redis_url.clone()).await);
let task_config = RedisTaskConfig {
redis_url: config_data.redis_url.clone(),
task_interval: config_data.redis_task_interval.clone(),
};
let shared_data = web::Data::new(AppConfigState {
req_host: config_data.req_host.clone(),
req_port: config_data.req_port.clone(),
scope_ref_list: config_data.scope_ref_list,
nls_service_instance_ref: config_data.nls_service_instance_ref,
lease_time: config_data.lease_time,
lease_renewal_factor: config_data.lease_renewal_factor,
rsa_client_token: config_data.rsa_client_token,
rsa_server_jwt: config_data.rsa_server_jwt,
});
// tls content
let public_key_der = pem::parse(&config_data.cert_https.public_key).unwrap().contents;
let private_key_der = pem::parse(&config_data.cert_https.private_key).unwrap().contents;
let public_key = vec![Certificate(public_key_der)];
let private_key = PrivateKey(private_key_der);
// configure certificate and private key used by https
let tls_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(public_key, private_key)
.unwrap();
// paste server addr and port
let bind_addr: SocketAddr = match format!("{}:{}", &config_data.server_addr, &config_data.server_port).parse() {
Ok(addr) => {
log::info!("starting HTTPS server at https://{}", addr);
addr
}
_ => {
log::error!("Error read bind address and port!");
std::process::exit(1);
}
};
let server = HttpServer::new(move || {
let leases_auth = HttpAuthentication::bearer(leases_validator);
let leasing_scope = web::scope("/leasing")
.service(get_all_leases)
.service(add_lessor)
.service(update_lessor)
.service(delete_all_leases)
.wrap(leases_auth);
App::new()
.wrap(middleware::Logger::default())
.service(hello)
.service(gen_client_token)
.service(origin_req)
.service(code_req)
.service(auth_token)
.service(leasing_scope)
.app_data(shared_data.clone())
.app_data(redis_client.clone())
})
.keep_alive(std::time::Duration::from_secs(5))
.bind_rustls(bind_addr, tls_config)?
.workers(2)
.run();
let server_handle = server.handle();
let task_shutdown_marker = Arc::new(AtomicBool::new(false));
// create my task
let server_task = tokio::spawn(server);
let worker_task = tokio::spawn(async_clean_lease(task_config, Arc::clone(&task_shutdown_marker)));
let shutdown = tokio::spawn(async move {
// listen for ctrl-c
tokio::signal::ctrl_c().await.unwrap();
// start shutdown of tasks
let server_stop = server_handle.stop(true);
task_shutdown_marker.store(true, Ordering::SeqCst);
// await shutdown of tasks
server_stop.await;
});
let _ = tokio::try_join!(server_task, worker_task, shutdown).expect("unable to join tasks");
Ok(())
}

251
src/utils.rs Normal file
View File

@ -0,0 +1,251 @@
use std::fs::OpenOptions;
use std::io::{Seek, Write};
use std::path::Path;
use uuid::Uuid;
use serde::{Deserialize, Serialize};
use obfstr::obfstr;
use crate::cert_tools::{gen_cert_rsa2048, gen_rsa2048};
#[derive(Clone)]
pub struct MyRsaKeyPair {
pub public_key: Vec<u8>,
pub private_key: Vec<u8>,
}
pub struct ConfigObj {
pub server_addr: String,
pub server_port: u16,
pub domain: String,
pub req_host: String,
pub req_port: u16,
pub redis_url: String,
pub redis_task_interval: u16,
pub scope_ref_list: Vec<Uuid>,
pub nls_service_instance_ref: Uuid,
pub lease_time: u16,
pub lease_renewal_factor: f32,
pub cert_https: MyRsaKeyPair,
pub rsa_client_token: MyRsaKeyPair,
pub rsa_server_jwt: MyRsaKeyPair,
}
#[derive(Serialize, Deserialize, Debug)]
struct KeyPairPath {
public_key: String,
private_key: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct ConfigData {
server_addr: String,
server_port: u16,
domain: String,
req_host: String,
req_port: u16,
redis_url: String,
redis_task_interval: u16,
scope_ref_list: Vec<Uuid>,
nls_service_instance_ref: Uuid,
lease_time: u16,
lease_renewal_factor: u8,
cert_https: KeyPairPath,
rsa_client_token: KeyPairPath,
}
fn read_keypair(kp_path: &KeyPairPath, domain: Option<String>) -> MyRsaKeyPair {
let public_key_path = Path::new(&kp_path.public_key);
let private_key_path = Path::new(&kp_path.private_key);
if (public_key_path.exists()) && (private_key_path.exists()) {
MyRsaKeyPair {
public_key: std::fs::read(&public_key_path).expect("failed to read key"),
private_key: std::fs::read(&private_key_path).expect("failed to read key"),
}
} else {
let gen_keypair;
match domain {
Some(domain) => {
gen_keypair = gen_cert_rsa2048(domain);
}
None => {
gen_keypair = gen_rsa2048();
}
}
let mut public_key_file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&public_key_path).expect("Unable to open public key file");
public_key_file.write(&gen_keypair.public_key).expect("Unable to write public key file");
let mut private_key_file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&private_key_path).expect("Unable to open private key file");
private_key_file.write(&gen_keypair.private_key).expect("Unable to write private key file");
gen_keypair
}
}
pub fn load_config() -> ConfigObj {
let data_path = Path::new(".").join("data");
let config_path = &data_path.join("config.json");
if !&data_path.exists() {
match std::fs::create_dir(&data_path) {
Ok(_) => {}
Err(_) => {
eprintln!("Failed to make data dir!");
std::process::exit(1);
}
};
}
let config_data: ConfigData = match OpenOptions::new()
.read(true)
.write(true)
.append(false)
.create(true)
.open(&config_path) {
Ok(mut file_obj) => {
let inner_data: ConfigData = match serde_json::from_reader(&file_obj) {
Ok(json_data) => json_data,
Err(deserialize_error) => {
log::error!("Failed to parse config: {:?}", deserialize_error);
log::warn!("Using Default Setting.");
let default_configs = ConfigData {
server_addr: "127.0.0.1".to_string(),
server_port: 443,
req_host: "127.0.0.1".to_string(),
req_port: 443,
domain: "localhost".to_string(),
redis_url: "redis://127.0.0.1/".to_string(),
redis_task_interval: 30,
scope_ref_list: vec![Uuid::new_v4(), Uuid::new_v4()],
nls_service_instance_ref: Uuid::new_v4(),
lease_time: 1440,
lease_renewal_factor: 35,
cert_https: KeyPairPath {
public_key: "./data/https_cert.pem".to_string(),
private_key: "./data/https_key.pem".to_string(),
},
rsa_client_token: KeyPairPath {
public_key: "./data/token_cert.pem".to_string(),
private_key: "./data/token_key.pem".to_string(),
},
};
// set write to start and remove nul
let _ = &file_obj.set_len(0);
let _ = file_obj.rewind().unwrap();
match serde_json::to_writer_pretty(&file_obj, &default_configs) {
Ok(_) => {
log::warn!("Saved default config data.")
}
Err(_) => {
log::error!("Failed to save config data!")
}
};
default_configs
}
};
inner_data
}
Err(file_error) => {
log::error!("Failed to open config file: {:?}", file_error);
std::process::exit(1);
}
};
if config_data.lease_renewal_factor > 100 {
log::error!("Config: failed to get lease_renewal_factor, value must be between 1 and 100!");
std::process::exit(1);
}
obfstr! {
let pub_key = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqd6EjBnZ68/Al/hQxCmz
5qW3eMXulTFYJb2WopgepjZCDvc2q/57qoekgVQEy9OwhXoXF8VnnTaoUaN7YZWA
r/woQ0Zkwe10FcWbT3Pju/DznqscmZPbSoru+SnUrxqZmzWeOo0q6l0w28tBZ2HC
+9ie95WHCfst/jVwZ+slsRAy7Uv5CwXeqIXubFhGwPV7+ICB2tmJiQPJcM+Y2tTK
FeaDyN9yKaUUjdjG80CGIKUnPdNCPEo/Cpf727rOCLl67kOd4mPmTrvyD0/nmREx
CQUSZt1smMFHR+uA11oN12I0yIy322gozwAyjd2r9Fok133/0EVTQqZ+ZmBExfor
3QIDAQAB
-----END PUBLIC KEY-----";
let priv_key = "-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqd6EjBnZ68/Al/hQxCmz5qW3eMXulTFYJb2WopgepjZCDvc2
q/57qoekgVQEy9OwhXoXF8VnnTaoUaN7YZWAr/woQ0Zkwe10FcWbT3Pju/Dznqsc
mZPbSoru+SnUrxqZmzWeOo0q6l0w28tBZ2HC+9ie95WHCfst/jVwZ+slsRAy7Uv5
CwXeqIXubFhGwPV7+ICB2tmJiQPJcM+Y2tTKFeaDyN9yKaUUjdjG80CGIKUnPdNC
PEo/Cpf727rOCLl67kOd4mPmTrvyD0/nmRExCQUSZt1smMFHR+uA11oN12I0yIy3
22gozwAyjd2r9Fok133/0EVTQqZ+ZmBExfor3QIDAQABAoIBAGEY7WD74eH15du4
N8p5H/kmHoKteRvUkdM41KLqGxLdDtNpIdocY+ntEO5P7VHpFgyl5g9Tak+mD35i
2ULFZ0Kw+v7BfRSQu3s6cfVvg+xI5ah7nKR4rK+mTMUl0QmqRcU/V8uWJ8LBNA1e
2GrUqdS1VOCmmwLsjbSyLSdpdSkI72pZYdP4FwF4MVfWu4gFrVf/vSxjy0l5wY/1
iCma0sjzS8npFP4Wf58PeBqbYUMJ/bCwPF+UYkx2xaRlAWWWtJsMdCQE4NBHqa1w
ZfHC5Y7oLjdYi0EDQeinqjnDIcKD/dedtDvrK5N3yL9D3VxiE0J2irzk9tNXvezf
S6lIqC0CgYEA333pptjB5DAZ+HihtziWSOvYPd3Ibz5BhUFqKJy9TjQh/IC7sVix
4ieKl4uLPWvURPZeWWJi56ncWQSwMLjTsVyIt49XKCzznLdHnHijb4kGHg+ycGpu
kT4pbjm+Dxts/ZifQHVlzmnRuBHb2S/s7Gk1XTn6AbCSZIUUPuLQ7pMCgYEAwpPg
t6Qto5M8cKG20x0SyBpkge3SJTXm4aahm+cQUAf3ylVX6MIqSTF6zwAjlP7mL8a8
ePFMHqwYZ2KRq/rrPwljmNHBMIx7Weh789S5q4meoa6yT2maQuA+7vRAmLqXwuLK
gz51mfAqFTbhGxLh2RpRicOK7CfC+byT3OF4kc8CgYAodGtR91SJkKdy0as8NjMF
+iMHd9jrQhKsI14rAcxGlqs8QLU48fwpGs08h1bqBFXFMe98MJIEqzumpXGbMCmp
pj1dNMYrEI/8YzTEPxYef2grEt5S+QEQq3bma+9aXrWI5hKVoWqPRZpfvmPUWZeC
Z7zwJil6GtM0/N3gUEBPnwKBgQCZeyoD0VZKtAY11emvhzxcaS0kq+JahbUUA2tw
3YepiU9043LPX/EZARWdGL/4dERAJWRfhf6EJz2stzyuyuMrOw276qCX2ggmuFKl
2AOJAqoFYRa3u1X6MIaT2Ejn8C9rg5c4hVkgTyfyyfIwd+l8Zd0xbPQ1KXwLoCuG
TLfdUwKBgHboiqjd22q6y753MUOFxr4TVHSOwYNTnzFjKyyjRZYXXz7r3e/xbVuW
Msgzaul+8rF3E83ZbR9u3Z2IURQZHKgA1e9rBMSf0dDnQ677oW/UubyoSHGwRuEV
BdLF6msAUzkXM1R1zS9Pk/5AVO54fj/HxYnsv+THMw8FvqGUMAz+
-----END RSA PRIVATE KEY-----";
}
let config_return = ConfigObj {
server_addr: config_data.server_addr.clone(),
server_port: config_data.server_port.clone(),
domain: config_data.domain.clone(),
req_host: config_data.req_host.clone(),
req_port: config_data.req_port.clone(),
redis_url: config_data.redis_url.clone(),
redis_task_interval: config_data.redis_task_interval.clone(),
scope_ref_list: config_data.scope_ref_list.clone(),
nls_service_instance_ref: config_data.nls_service_instance_ref.clone(),
lease_time: config_data.lease_time.clone(),
lease_renewal_factor: f32::from(config_data.lease_renewal_factor.clone()) / 100.0,
cert_https: read_keypair(&config_data.cert_https, Option::from(config_data.domain.clone())),
rsa_client_token: read_keypair(&config_data.rsa_client_token, None),
rsa_server_jwt: MyRsaKeyPair {
public_key: pub_key.as_bytes().to_vec(),
private_key: priv_key.as_bytes().to_vec(),
},
};
config_return
}
pub async fn prepare_redis(connection_url: String) -> redis::Client {
match redis::Client::open(connection_url) {
Ok(redis_client) => {
let _ = match redis_client.get_async_connection().await {
Ok(conn)=> conn,
Err(connection_error) =>{
log::error!("Failed to get redis connection! Cause: {}", connection_error);
std::process::exit(1);
}
};
redis_client
},
Err(client_err) => {
log::error!("Failed to open redis client! Cause: {}", client_err);
std::process::exit(1);
}
}
}