use tonic::{transport::{Server, ServerTlsConfig, Identity}, metadata::MetadataValue, Request, Response, Status};
use tonic::service::interceptor::InterceptedService;
use tonic_health::server::HealthReporter;
use rfkpos::rfkpos_server::{Rfkpos, RfkposServer};
use rfkpos::{CreateTransaction, GetTransaction, TransactionReturn, Database, Transaction, Purchase, transaction, Timestamp};
use std::time::Duration;
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::io;
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use openssl::sign::{Signer, Verifier};
use openssl::ec::{EcKey, EcGroup, EcGroupRef};
use openssl::pkey::{ PKey, Private };
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use prost::Message;
mod rfkpos {
include!("rfkpos.rs");
pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =
tonic::include_file_descriptor_set!("greeter_descriptor");
}
#[derive(Default)]
pub struct RFKPOSImpl {
db: Arc<Mutex<Database>>,
tokens: Arc<Mutex<LinkedList<String>>>,
}
pub struct DataValidator {
group: EcGroup,
key_priv: PKey<Private>,
}
impl DataValidator {
pub fn new() -> Result<Self, io::Error> {
let group = EcGroup::from_curve_name(Nid::SECP256K1)?;
let key = EcKey::generate(group.as_ref())?;
let key_priv = PKey::from_ec_key(key)?;
Ok(Self { group: group, key_priv: key_priv })
}
}
fn new_token(tokens: Arc<Mutex<LinkedList<String>>>) -> String {
let new_token: String = thread_rng().sample_iter(Alphanumeric).take(32).map(char::from).collect();
let mut tokens = tokens.lock().unwrap();
tokens.push_back(new_token.clone());
while tokens.len() > 100 { tokens.pop_front(); }
new_token
}
#[tonic::async_trait]
impl Rfkpos for RFKPOSImpl {
async fn create(&self, request: Request<CreateTransaction>) -> Result<Response<TransactionReturn>, Status> {
let remote_addr = request.remote_addr();
let data = request.into_inner();
println!("Request from {:?} {:?}", remote_addr, data);
let data_validator = DataValidator::new();
let mut transaction = data.transaction.ok_or_else(|| Status::invalid_argument("transaction not specified"))?;
let tid = transaction.tid;
self.db.lock().unwrap().transactions.insert(tid, transaction);
Ok(Response::new(TransactionReturn {
token: new_token(self.tokens.clone()),
tid: tid,
}))
}
async fn get(&self, request: Request<GetTransaction>) -> Result<Response<TransactionReturn>, Status> {
println!("Request from {:?}", request.remote_addr());
Err(Status::internal("Not implemented"))
}
}
async fn rfkpos_service_status(mut reporter: HealthReporter) {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
reporter.set_serving::<RfkposServer<RFKPOSImpl>>().await;
}
}
struct Authenticator {
token: String,
db: Arc<Mutex<Database>>,
}
impl Authenticator {
pub fn new(db: Arc<Mutex<Database>>) -> Self {
Self { token: "Bearer test-token".to_string(), db: db }
}
fn get_token(&self) -> &str {
self.token.as_str()
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse().unwrap();
let cert = tokio::fs::read("server.pem").await?;
let key = tokio::fs::read("server.key").await?;
let identity = Identity::from_pem(cert, key);
let rfkpos = RFKPOSImpl::default();
let authenticator = Arc::new(Mutex::new(Authenticator::new(rfkpos.db.clone())));
let rfkpos_auth = Arc::clone(&authenticator);
let rfkpos_svc = RfkposServer::with_interceptor(rfkpos, move |r| check_auth(r, rfkpos_auth.clone()));
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(rfkpos::FILE_DESCRIPTOR_SET)
.build()
.unwrap();
let reflection_service_auth = Arc::clone(&authenticator);
let reflection_service_svc = InterceptedService::new(reflection_service, move |r| check_auth(r, reflection_service_auth.clone()));
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
health_reporter.set_serving::<RfkposServer<RFKPOSImpl>>().await;
tokio::spawn(rfkpos_service_status(health_reporter.clone()));
println!("RFKPOS server listening on {}", addr);
Server::builder()
.tls_config(ServerTlsConfig::new().identity(identity))?
.add_service(health_service)
.add_service(rfkpos_svc)
.add_service(reflection_service_svc) .serve(addr)
.await?;
Ok(())
}
fn check_auth(req: Request<()>, authenticator: Arc<Mutex<Authenticator>>) -> Result<Request<()>, Status> {
let token: MetadataValue<_> = authenticator.lock().unwrap().get_token().parse().unwrap();
let req_token = req.metadata().get("authorization");
println!("DEBUG: authorization token - {:?}", req_token);
match req.metadata().get("authorization") {
Some(t) if token == t => Ok(req),
_ => Err(Status::unauthenticated("No malid auth token")),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_test() {
let mut trans = Transaction::default();
trans.tid = 1;
trans.parent = 2;
trans.ts = 20;
let mut purchase = Purchase::default();
purchase.ts = Some(Timestamp { year: 2022, month: 12, day: 1, hour: 10, minute: 2, second: 3, tz: 3600 });
trans.transaction_type = Some(transaction::TransactionType::Purchase(purchase));
trans.signature = Some("test".to_string());
let sig = trans.signature;
trans.signature = None;
let mut buf = vec![];
let res = trans.encode(&mut buf);
println!("DEBUG: {:?} {:?} {:?} {:?}", trans, sig, buf, res);
}
}