mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-10-01 05:44:52 +02:00
Rustfmt
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
extern crate krill_client;
|
||||
|
||||
use krill_client::KrillClient;
|
||||
use krill_client::options::Options;
|
||||
use krill_client::report::ReportFormat;
|
||||
use krill_client::KrillClient;
|
||||
|
||||
fn main() {
|
||||
match Options::from_args() {
|
||||
@@ -17,10 +17,10 @@ fn main() {
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+46
-82
@@ -1,37 +1,22 @@
|
||||
use std::io;
|
||||
use rpki::uri;
|
||||
use std::io;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use krill_commons::util::file;
|
||||
use krill_commons::util::httpclient;
|
||||
use krill_commons::api::admin::{
|
||||
ParentCaContact,
|
||||
PublisherDetails,
|
||||
PublisherList,
|
||||
PublisherRequest,
|
||||
Token,
|
||||
};
|
||||
use krill_commons::api::ca::{TrustAnchorInfo};
|
||||
use krill_commons::remote::api::{
|
||||
ClientAuth,
|
||||
ClientInfo,
|
||||
ParentCaContact, PublisherDetails, PublisherList, PublisherRequest, Token,
|
||||
};
|
||||
use krill_commons::api::ca::TrustAnchorInfo;
|
||||
use krill_commons::remote::api::{ClientAuth, ClientInfo};
|
||||
use krill_commons::remote::rfc8183;
|
||||
use krill_commons::remote::rfc8183::RepositoryResponse;
|
||||
use krill_commons::util::file;
|
||||
use krill_commons::util::httpclient;
|
||||
|
||||
use crate::report::{
|
||||
ApiResponse,
|
||||
ReportError
|
||||
};
|
||||
use crate::options::{
|
||||
Options,
|
||||
CaCommand,
|
||||
Command,
|
||||
PublishersCommand,
|
||||
Rfc8181Command,
|
||||
TrustAnchorCommand
|
||||
CaCommand, Command, Options, PublishersCommand, Rfc8181Command, TrustAnchorCommand,
|
||||
};
|
||||
use crate::report::{ApiResponse, ReportError};
|
||||
|
||||
/// Command line tool for Krill admin tasks
|
||||
pub struct KrillClient {
|
||||
@@ -40,7 +25,6 @@ pub struct KrillClient {
|
||||
}
|
||||
|
||||
impl KrillClient {
|
||||
|
||||
/// Delegates the options to be processed, and reports the response
|
||||
/// back to the user. Note that error reporting is handled by CLI.
|
||||
pub fn report(options: Options) -> Result<(), Error> {
|
||||
@@ -58,8 +42,8 @@ impl KrillClient {
|
||||
/// and client.
|
||||
pub fn process(options: Options) -> Result<ApiResponse, Error> {
|
||||
let client = KrillClient {
|
||||
server: options.server,
|
||||
token: options.token,
|
||||
server: options.server,
|
||||
token: options.token,
|
||||
};
|
||||
match options.command {
|
||||
Command::Health => client.health(),
|
||||
@@ -67,15 +51,12 @@ impl KrillClient {
|
||||
Command::CertAuth(cmd) => client.certauth(cmd),
|
||||
Command::Publishers(cmd) => client.publishers(cmd),
|
||||
Command::Rfc8181(cmd) => client.rfc8181(cmd),
|
||||
Command::NotSet => Err(Error::MissingCommand)
|
||||
Command::NotSet => Err(Error::MissingCommand),
|
||||
}
|
||||
}
|
||||
|
||||
fn health(&self) -> Result<ApiResponse, Error> {
|
||||
httpclient::get_ok(
|
||||
&self.resolve_uri("api/v1/health"),
|
||||
Some(&self.token)
|
||||
)?;
|
||||
httpclient::get_ok(&self.resolve_uri("api/v1/health"), Some(&self.token))?;
|
||||
Ok(ApiResponse::Health)
|
||||
}
|
||||
|
||||
@@ -85,22 +66,21 @@ impl KrillClient {
|
||||
let uri = self.resolve_uri("api/v1/trustanchor");
|
||||
httpclient::post_empty(&uri, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
}
|
||||
TrustAnchorCommand::Show => {
|
||||
let uri = self.resolve_uri("api/v1/trustanchor");
|
||||
let ta: TrustAnchorInfo = self.get_json(&uri)?;
|
||||
let ta: TrustAnchorInfo = self.get_json(&uri)?;
|
||||
Ok(ApiResponse::TrustAnchorInfo(ta))
|
||||
},
|
||||
}
|
||||
TrustAnchorCommand::Publish => {
|
||||
let uri = self.resolve_uri("api/v1/republish");
|
||||
httpclient::post_empty(&uri, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
}
|
||||
TrustAnchorCommand::AddChild(req) => {
|
||||
let uri = self.resolve_uri("api/v1/trustanchor/children");
|
||||
let info: ParentCaContact = httpclient::post_json_with_response(
|
||||
&uri, req, Some(&self.token)
|
||||
)?;
|
||||
let info: ParentCaContact =
|
||||
httpclient::post_json_with_response(&uri, req, Some(&self.token))?;
|
||||
Ok(ApiResponse::ParentCaInfo(info))
|
||||
}
|
||||
}
|
||||
@@ -113,29 +93,25 @@ impl KrillClient {
|
||||
let uri = self.resolve_uri(&uri);
|
||||
httpclient::post_json(&uri, parent, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
}
|
||||
CaCommand::ChildRequest(handle) => {
|
||||
let uri = format!("api/v1/cas/{}/child_request", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
let xml = httpclient::get_text(
|
||||
&uri,
|
||||
"application/xml",
|
||||
Some(&self.token)
|
||||
)?;
|
||||
let xml = httpclient::get_text(&uri, "application/xml", Some(&self.token))?;
|
||||
|
||||
let req = rfc8183::ChildRequest::validate(xml.as_bytes())?;
|
||||
Ok(ApiResponse::Rfc8183ChildRequest(req))
|
||||
},
|
||||
}
|
||||
CaCommand::Init(init) => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
httpclient::post_json(&uri, init, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
}
|
||||
CaCommand::List => {
|
||||
let uri = self.resolve_uri("api/v1/cas");
|
||||
let cas = self.get_json(&uri)?;
|
||||
Ok(ApiResponse::CertAuths(cas))
|
||||
},
|
||||
}
|
||||
CaCommand::Show(handle) => {
|
||||
let uri = format!("api/v1/cas/{}", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
@@ -146,36 +122,29 @@ impl KrillClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn publishers(
|
||||
&self,
|
||||
command: PublishersCommand,
|
||||
) -> Result<ApiResponse, Error> {
|
||||
fn publishers(&self, command: PublishersCommand) -> Result<ApiResponse, Error> {
|
||||
match command {
|
||||
PublishersCommand::List => {
|
||||
let list: PublisherList = self.get_json(&self.resolve_uri("api/v1/publishers"))?;
|
||||
Ok(ApiResponse::PublisherList(list))
|
||||
},
|
||||
}
|
||||
PublishersCommand::Add(add) => {
|
||||
let pbl = PublisherRequest::new(
|
||||
add.handle,
|
||||
add.token,
|
||||
add.base_uri
|
||||
);
|
||||
let pbl = PublisherRequest::new(add.handle, add.token, add.base_uri);
|
||||
self.add_publisher(pbl)
|
||||
},
|
||||
}
|
||||
PublishersCommand::Deactivate(handle) => {
|
||||
let uri = format!("api/v1/publishers/{}", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
httpclient::delete(&uri, Some(&self.token))?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
}
|
||||
PublishersCommand::Details(handle) => {
|
||||
let uri = format!("api/v1/publishers/{}", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
|
||||
let details: PublisherDetails = self.get_json(&uri)?;
|
||||
Ok(ApiResponse::PublisherDetails(details))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +152,7 @@ impl KrillClient {
|
||||
httpclient::post_json(
|
||||
&self.resolve_uri("api/v1/publishers"),
|
||||
pbl,
|
||||
Some(&self.token)
|
||||
Some(&self.token),
|
||||
)?;
|
||||
|
||||
Ok(ApiResponse::Empty)
|
||||
@@ -196,7 +165,7 @@ impl KrillClient {
|
||||
let list: Vec<ClientInfo> = self.get_json(&uri)?;
|
||||
|
||||
Ok(ApiResponse::Rfc8181ClientList(list))
|
||||
},
|
||||
}
|
||||
Rfc8181Command::RepoRes(handle) => {
|
||||
let uri = format!("api/v1/rfc8181/{}/response.xml", handle);
|
||||
let uri = self.resolve_uri(&uri);
|
||||
@@ -206,9 +175,8 @@ impl KrillClient {
|
||||
|
||||
let res = RepositoryResponse::validate(xml.as_bytes())?;
|
||||
Ok(ApiResponse::Rfc8183RepositoryResponse(res))
|
||||
},
|
||||
}
|
||||
Rfc8181Command::Add(details) => {
|
||||
|
||||
let xml = file::read(&details.xml)?;
|
||||
let pr = rfc8183::PublisherRequest::validate(xml.as_ref())?;
|
||||
|
||||
@@ -222,7 +190,7 @@ impl KrillClient {
|
||||
httpclient::post_json(
|
||||
&self.resolve_uri("api/v1/rfc8181/clients"),
|
||||
info,
|
||||
Some(&self.token)
|
||||
Some(&self.token),
|
||||
)?;
|
||||
|
||||
Ok(ApiResponse::Empty)
|
||||
@@ -234,14 +202,8 @@ impl KrillClient {
|
||||
format!("{}{}", &self.server, path)
|
||||
}
|
||||
|
||||
fn get_json<T: DeserializeOwned>(
|
||||
&self,
|
||||
uri: &str,
|
||||
) -> Result<T, Error> {
|
||||
httpclient::get_json(
|
||||
&uri,
|
||||
Some(&self.token)
|
||||
).map_err(Error::HttpClientError)
|
||||
fn get_json<T: DeserializeOwned>(&self, uri: &str) -> Result<T, Error> {
|
||||
httpclient::get_json(&uri, Some(&self.token)).map_err(Error::HttpClientError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,30 +211,32 @@ impl KrillClient {
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
#[display(fmt="No valid command given, see --help")]
|
||||
#[display(fmt = "No valid command given, see --help")]
|
||||
MissingCommand,
|
||||
|
||||
#[display(fmt="Server is not available.")]
|
||||
#[display(fmt = "Server is not available.")]
|
||||
ServerDown,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
HttpClientError(httpclient::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
ReportError(ReportError),
|
||||
|
||||
#[display(fmt="Can't read file: {}", _0)]
|
||||
#[display(fmt = "Can't read file: {}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="Empty response received from server")]
|
||||
#[display(fmt = "Empty response received from server")]
|
||||
EmptyResponse,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
Rfc8183(rfc8183::Error),
|
||||
}
|
||||
|
||||
impl From<httpclient::Error> for Error {
|
||||
fn from(e: httpclient::Error) -> Self { Error::HttpClientError(e) }
|
||||
fn from(e: httpclient::Error) -> Self {
|
||||
Error::HttpClientError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
@@ -291,4 +255,4 @@ impl From<rfc8183::Error> for Error {
|
||||
fn from(e: rfc8183::Error) -> Error {
|
||||
Error::Rfc8183(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
extern crate clap;
|
||||
#[macro_use] extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
extern crate krill_commons;
|
||||
extern crate rpki;
|
||||
extern crate serde;
|
||||
@@ -8,5 +9,5 @@ pub mod options;
|
||||
pub mod report;
|
||||
|
||||
mod client;
|
||||
pub use client::KrillClient;
|
||||
pub use client::Error;
|
||||
pub use client::KrillClient;
|
||||
|
||||
+47
-59
@@ -1,17 +1,17 @@
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use rpki::uri;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use krill_commons::api::admin::{AddChildRequest, CertAuthInit, CertAuthPubMode, Handle, AddParentRequest, ParentCaContact, Token, ChildAuthRequest};
|
||||
use krill_commons::api::admin::{
|
||||
AddChildRequest, AddParentRequest, CertAuthInit, CertAuthPubMode, ChildAuthRequest, Handle,
|
||||
ParentCaContact, Token,
|
||||
};
|
||||
use krill_commons::api::ca::ResourceSet;
|
||||
|
||||
use crate::report::{
|
||||
ReportFormat,
|
||||
ReportError
|
||||
};
|
||||
use krill_commons::util::file;
|
||||
use crate::report::{ReportError, ReportFormat};
|
||||
use krill_commons::remote::rfc8183;
|
||||
use krill_commons::util::file;
|
||||
use std::io;
|
||||
|
||||
/// This type holds all the necessary data to connect to a Krill daemon, and
|
||||
@@ -22,7 +22,7 @@ pub struct Options {
|
||||
pub server: uri::Https,
|
||||
pub token: Token,
|
||||
pub format: ReportFormat,
|
||||
pub command: Command
|
||||
pub command: Command,
|
||||
}
|
||||
|
||||
impl Options {
|
||||
@@ -31,13 +31,13 @@ impl Options {
|
||||
}
|
||||
|
||||
/// Creates a new Options explicitly (useful for testing)
|
||||
pub fn new(
|
||||
server: uri::Https,
|
||||
token: &str,
|
||||
format: ReportFormat,
|
||||
command: Command
|
||||
) -> Self {
|
||||
Options { server, token: Token::from(token), format, command }
|
||||
pub fn new(server: uri::Https, token: &str, format: ReportFormat, command: Command) -> Self {
|
||||
Options {
|
||||
server,
|
||||
token: Token::from(token),
|
||||
format,
|
||||
command,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Options from command line args (useful for cli)
|
||||
@@ -344,7 +344,6 @@ impl Options {
|
||||
}
|
||||
if let Some(m) = m.subcommand_matches("children") {
|
||||
if let Some(m) = m.subcommand_matches("add") {
|
||||
|
||||
let asn = m.value_of("asn").unwrap_or("");
|
||||
let ipv4 = m.value_of("ipv4").unwrap_or("");
|
||||
let ipv6 = m.value_of("ipv6").unwrap_or("");
|
||||
@@ -356,9 +355,7 @@ impl Options {
|
||||
|
||||
let auth = ChildAuthRequest::Embedded(token);
|
||||
|
||||
let req = AddChildRequest::new(
|
||||
handle, res, auth
|
||||
);
|
||||
let req = AddChildRequest::new(handle, res, auth);
|
||||
command = Command::TrustAnchor(TrustAnchorCommand::AddChild(req))
|
||||
}
|
||||
|
||||
@@ -380,16 +377,11 @@ impl Options {
|
||||
|
||||
let auth = ChildAuthRequest::Rfc8183(cr);
|
||||
|
||||
let req = AddChildRequest::new(
|
||||
handle, res, auth
|
||||
);
|
||||
let req = AddChildRequest::new(handle, res, auth);
|
||||
command = Command::TrustAnchor(TrustAnchorCommand::AddChild(req))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if let Some(m) = matches.subcommand_matches("cas") {
|
||||
@@ -421,13 +413,11 @@ impl Options {
|
||||
if let Some(m) = m.subcommand_matches("embedded") {
|
||||
let token = Token::from(m.value_of("token").unwrap());
|
||||
|
||||
let contact = ParentCaContact::Embedded(parent.clone(),
|
||||
token);
|
||||
let contact = ParentCaContact::Embedded(parent.clone(), token);
|
||||
|
||||
let req = AddParentRequest::new(parent, contact);
|
||||
|
||||
command = Command::CertAuth(
|
||||
CaCommand::AddParent(handle, req))
|
||||
command = Command::CertAuth(CaCommand::AddParent(handle, req))
|
||||
} else if let Some(m) = m.subcommand_matches("rfc6492") {
|
||||
let xml_path = m.value_of("xml").unwrap();
|
||||
let xml = PathBuf::from(xml_path);
|
||||
@@ -437,13 +427,8 @@ impl Options {
|
||||
let contact = ParentCaContact::Rfc6492(pr);
|
||||
let req = AddParentRequest::new(parent, contact);
|
||||
|
||||
command = Command::CertAuth(
|
||||
CaCommand::AddParent(handle, req)
|
||||
)
|
||||
|
||||
command = Command::CertAuth(CaCommand::AddParent(handle, req))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -457,10 +442,12 @@ impl Options {
|
||||
let base_uri = uri::Rsync::from_str(m.value_of("uri").unwrap())?;
|
||||
let token = Token::from(m.value_of("token").unwrap());
|
||||
|
||||
let add = AddPublisher { handle, base_uri, token };
|
||||
command = Command::Publishers(
|
||||
PublishersCommand::Add(add)
|
||||
);
|
||||
let add = AddPublisher {
|
||||
handle,
|
||||
base_uri,
|
||||
token,
|
||||
};
|
||||
command = Command::Publishers(PublishersCommand::Add(add));
|
||||
}
|
||||
if let Some(m) = m.subcommand_matches("details") {
|
||||
let handle = m.value_of("handle").unwrap();
|
||||
@@ -481,15 +468,12 @@ impl Options {
|
||||
let xml_path = m.value_of("xml").unwrap();
|
||||
let xml = PathBuf::from(xml_path);
|
||||
|
||||
command = Command::Rfc8181(
|
||||
Rfc8181Command::Add(AddRfc8181Client{ xml })
|
||||
)
|
||||
command = Command::Rfc8181(Rfc8181Command::Add(AddRfc8181Client { xml }))
|
||||
}
|
||||
if let Some(m) = m.subcommand_matches("repo-res") {
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
let handle = Handle::from(m.value_of("handle").unwrap());
|
||||
command = Command::Rfc8181(Rfc8181Command::RepoRes(handle));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let server = matches.value_of("server").unwrap(); // required
|
||||
@@ -502,7 +486,12 @@ impl Options {
|
||||
format = ReportFormat::from_str(fmt)?;
|
||||
}
|
||||
|
||||
Ok(Options { server, token, format, command })
|
||||
Ok(Options {
|
||||
server,
|
||||
token,
|
||||
format,
|
||||
command,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +503,7 @@ pub enum Command {
|
||||
TrustAnchor(TrustAnchorCommand),
|
||||
CertAuth(CaCommand),
|
||||
Publishers(PublishersCommand),
|
||||
Rfc8181(Rfc8181Command)
|
||||
Rfc8181(Rfc8181Command),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -523,7 +512,7 @@ pub enum TrustAnchorCommand {
|
||||
Init,
|
||||
Show,
|
||||
Publish,
|
||||
AddChild(AddChildRequest)
|
||||
AddChild(AddChildRequest),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -536,48 +525,47 @@ pub enum CaCommand {
|
||||
Show(Handle),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PublishersCommand {
|
||||
Add(AddPublisher),
|
||||
Details(String),
|
||||
Deactivate(String),
|
||||
List
|
||||
List,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AddPublisher {
|
||||
pub handle: Handle,
|
||||
pub handle: Handle,
|
||||
pub base_uri: uri::Rsync,
|
||||
pub token: Token
|
||||
pub token: Token,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Rfc8181Command {
|
||||
List,
|
||||
Add(AddRfc8181Client),
|
||||
RepoRes(Handle)
|
||||
RepoRes(Handle),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct AddRfc8181Client {
|
||||
pub xml: PathBuf
|
||||
pub xml: PathBuf,
|
||||
}
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
UriError(uri::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
ReportError(ReportError),
|
||||
|
||||
#[display(fmt="Invalid RFC8183 XML: {}", _0)]
|
||||
#[display(fmt = "Invalid RFC8183 XML: {}", _0)]
|
||||
Rfc8183(rfc8183::Error),
|
||||
}
|
||||
|
||||
@@ -603,4 +591,4 @@ impl From<ReportError> for Error {
|
||||
fn from(e: ReportError) -> Self {
|
||||
Error::ReportError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+57
-95
@@ -1,10 +1,11 @@
|
||||
use std::str::{FromStr, from_utf8_unchecked};
|
||||
use krill_commons::api::admin::{PublisherDetails, PublisherList, ParentCaContact};
|
||||
use krill_commons::api::ca::{TrustAnchorInfo, CertAuthList, CertAuthInfo, CaParentsInfo, CurrentObjects};
|
||||
use krill_commons::api::admin::{ParentCaContact, PublisherDetails, PublisherList};
|
||||
use krill_commons::api::ca::{
|
||||
CaParentsInfo, CertAuthInfo, CertAuthList, CurrentObjects, TrustAnchorInfo,
|
||||
};
|
||||
use krill_commons::remote::api::ClientInfo;
|
||||
use krill_commons::remote::rfc8183::RepositoryResponse;
|
||||
use krill_commons::remote::rfc8183;
|
||||
|
||||
use krill_commons::remote::rfc8183::RepositoryResponse;
|
||||
use std::str::{from_utf8_unchecked, FromStr};
|
||||
|
||||
//------------ ApiResponse ---------------------------------------------------
|
||||
|
||||
@@ -28,15 +29,12 @@ pub enum ApiResponse {
|
||||
Rfc8183RepositoryResponse(rfc8183::RepositoryResponse),
|
||||
Rfc8183ChildRequest(rfc8183::ChildRequest),
|
||||
|
||||
Empty, // Typically a successful post just gets an empty 200 response
|
||||
GenericBody(String) // For when the server echos Json to a successful post
|
||||
Empty, // Typically a successful post just gets an empty 200 response
|
||||
GenericBody(String), // For when the server echos Json to a successful post
|
||||
}
|
||||
|
||||
impl ApiResponse {
|
||||
pub fn report(
|
||||
&self,
|
||||
fmt: ReportFormat
|
||||
) -> Result<Option<String>, ReportError> {
|
||||
pub fn report(&self, fmt: ReportFormat) -> Result<Option<String>, ReportError> {
|
||||
if fmt == ReportFormat::None {
|
||||
Ok(None)
|
||||
} else {
|
||||
@@ -47,38 +45,18 @@ impl ApiResponse {
|
||||
} else {
|
||||
Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
},
|
||||
ApiResponse::TrustAnchorInfo(ta) => {
|
||||
Ok(Some(ta.report(fmt)?))
|
||||
},
|
||||
ApiResponse::CertAuths(list) => {
|
||||
Ok(Some(list.report(fmt)?))
|
||||
},
|
||||
ApiResponse::CertAuthInfo(info) => {
|
||||
Ok(Some(info.report(fmt)?))
|
||||
},
|
||||
ApiResponse::ParentCaInfo(info) => {
|
||||
Ok(Some(info.report(fmt)?))
|
||||
},
|
||||
ApiResponse::PublisherList(list) => {
|
||||
Ok(Some(list.report(fmt)?))
|
||||
},
|
||||
ApiResponse::PublisherDetails(details) => {
|
||||
Ok(Some(details.report(fmt)?))
|
||||
}
|
||||
ApiResponse::Rfc8181ClientList(list) => {
|
||||
Ok(Some(list.report(fmt)?))
|
||||
}
|
||||
ApiResponse::Rfc8183ChildRequest(req) => {
|
||||
Ok(Some(req.report(fmt)?))
|
||||
}
|
||||
ApiResponse::Rfc8183RepositoryResponse(res) => {
|
||||
Ok(Some(res.report(fmt)?))
|
||||
}
|
||||
ApiResponse::GenericBody(body) => {
|
||||
Ok(Some(body.clone()))
|
||||
}
|
||||
ApiResponse::Empty => Ok(None)
|
||||
ApiResponse::TrustAnchorInfo(ta) => Ok(Some(ta.report(fmt)?)),
|
||||
ApiResponse::CertAuths(list) => Ok(Some(list.report(fmt)?)),
|
||||
ApiResponse::CertAuthInfo(info) => Ok(Some(info.report(fmt)?)),
|
||||
ApiResponse::ParentCaInfo(info) => Ok(Some(info.report(fmt)?)),
|
||||
ApiResponse::PublisherList(list) => Ok(Some(list.report(fmt)?)),
|
||||
ApiResponse::PublisherDetails(details) => Ok(Some(details.report(fmt)?)),
|
||||
ApiResponse::Rfc8181ClientList(list) => Ok(Some(list.report(fmt)?)),
|
||||
ApiResponse::Rfc8183ChildRequest(req) => Ok(Some(req.report(fmt)?)),
|
||||
ApiResponse::Rfc8183RepositoryResponse(res) => Ok(Some(res.report(fmt)?)),
|
||||
ApiResponse::GenericBody(body) => Ok(Some(body.clone())),
|
||||
ApiResponse::Empty => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +71,7 @@ pub enum ReportFormat {
|
||||
None,
|
||||
Json,
|
||||
Text,
|
||||
Xml
|
||||
Xml,
|
||||
}
|
||||
|
||||
impl FromStr for ReportFormat {
|
||||
@@ -104,26 +82,24 @@ impl FromStr for ReportFormat {
|
||||
"none" => Ok(ReportFormat::None),
|
||||
"json" => Ok(ReportFormat::Json),
|
||||
"text" => Ok(ReportFormat::Text),
|
||||
"xml" => Ok(ReportFormat::Xml),
|
||||
_ => Err(ReportError::UnrecognisedFormat(s.to_string()))
|
||||
"xml" => Ok(ReportFormat::Xml),
|
||||
_ => Err(ReportError::UnrecognisedFormat(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ReportError ---------------------------------------------------
|
||||
|
||||
/// This type defines possible Errors for KeyStore
|
||||
#[derive(Debug, Display)]
|
||||
pub enum ReportError {
|
||||
#[display(fmt="This report format is not supported for this data")]
|
||||
#[display(fmt = "This report format is not supported for this data")]
|
||||
UnsupportedFormat,
|
||||
|
||||
#[display(fmt="This report format is not recognised: {}", _0)]
|
||||
UnrecognisedFormat(String)
|
||||
#[display(fmt = "This report format is not recognised: {}", _0)]
|
||||
UnrecognisedFormat(String),
|
||||
}
|
||||
|
||||
|
||||
//------------ Report --------------------------------------------------------
|
||||
|
||||
/// This trait should be implemented by all api responses, so that the
|
||||
@@ -137,7 +113,7 @@ impl Report for TrustAnchorInfo {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
let mut res = String::new();
|
||||
|
||||
@@ -168,16 +144,14 @@ impl Report for TrustAnchorInfo {
|
||||
res.push_str(&format!(" v6: {}\n", inrs.v6()));
|
||||
res.push_str("\n");
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
res.push_str("<none>");
|
||||
}
|
||||
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,7 +161,7 @@ impl Report for CertAuthList {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
let mut res = String::new();
|
||||
for ca in self.cas() {
|
||||
@@ -195,8 +169,8 @@ impl Report for CertAuthList {
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +180,7 @@ impl Report for CertAuthInfo {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
let mut res = String::new();
|
||||
|
||||
@@ -236,7 +210,6 @@ impl Report for CertAuthInfo {
|
||||
res.push_str(&format!("IPv4: {}\n", inrs.v4()));
|
||||
res.push_str(&format!("IPv6: {}\n", inrs.v6()));
|
||||
|
||||
|
||||
res.push_str("Current objects:\n");
|
||||
print_objects(&mut res, key.current_set().objects());
|
||||
res.push_str("\n");
|
||||
@@ -254,7 +227,6 @@ impl Report for CertAuthInfo {
|
||||
res.push_str(&format!(" v6: {}\n", inrs.v6()));
|
||||
res.push_str("\n");
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
res.push_str("<none>");
|
||||
@@ -262,7 +234,7 @@ impl Report for CertAuthInfo {
|
||||
|
||||
res.push_str("TAL:\n");
|
||||
res.push_str(&format!("{}\n", tal));
|
||||
},
|
||||
}
|
||||
CaParentsInfo::Parents(map) => {
|
||||
for info in map.values() {
|
||||
res.push_str(&format!("Parent: {}\n", info.contact()));
|
||||
@@ -296,14 +268,13 @@ impl Report for CertAuthInfo {
|
||||
res.push_str(" OLD unrevoked key exists!\n");
|
||||
res.push_str("\n");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,11 +284,9 @@ impl Report for ParentCaContact {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
ReportFormat::Text => {
|
||||
Ok(self.to_string())
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
ReportFormat::Text => Ok(self.to_string()),
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,14 +296,14 @@ impl Report for PublisherList {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
let mut res = String::new();
|
||||
|
||||
res.push_str("Publishers: ");
|
||||
let mut first = true;
|
||||
for p in self.publishers() {
|
||||
if ! first {
|
||||
if !first {
|
||||
res.push_str(", ");
|
||||
} else {
|
||||
first = false;
|
||||
@@ -342,8 +311,8 @@ impl Report for PublisherList {
|
||||
res.push_str(p.id());
|
||||
}
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,9 +322,8 @@ impl Report for PublisherDetails {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
|
||||
let mut res = String::new();
|
||||
|
||||
res.push_str("handle: ");
|
||||
@@ -367,8 +335,8 @@ impl Report for PublisherDetails {
|
||||
res.push_str("\n");
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,7 +346,7 @@ impl Report for Vec<ClientInfo> {
|
||||
match format {
|
||||
ReportFormat::Default | ReportFormat::Json => {
|
||||
Ok(serde_json::to_string_pretty(self).unwrap())
|
||||
},
|
||||
}
|
||||
ReportFormat::Text => {
|
||||
let mut res = String::new();
|
||||
|
||||
@@ -388,13 +356,11 @@ impl Report for Vec<ClientInfo> {
|
||||
let auth = client.auth();
|
||||
let ski = auth.cert().ski_hex();
|
||||
|
||||
res.push_str(
|
||||
&format!(" Handle: {}, Cert (ski): {}\n", handle, ski)
|
||||
);
|
||||
res.push_str(&format!(" Handle: {}, Cert (ski): {}\n", handle, ski));
|
||||
}
|
||||
Ok(res)
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,13 +370,11 @@ impl Report for RepositoryResponse {
|
||||
match format {
|
||||
ReportFormat::Text | ReportFormat::Xml | ReportFormat::Default => {
|
||||
let bytes = self.encode_vec();
|
||||
let xml = unsafe {
|
||||
from_utf8_unchecked(&bytes)
|
||||
};
|
||||
let xml = unsafe { from_utf8_unchecked(&bytes) };
|
||||
|
||||
Ok(xml.to_string())
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,13 +384,11 @@ impl Report for rfc8183::ChildRequest {
|
||||
match format {
|
||||
ReportFormat::Text | ReportFormat::Xml | ReportFormat::Default => {
|
||||
let bytes = self.encode_vec();
|
||||
let xml = unsafe {
|
||||
from_utf8_unchecked(&bytes)
|
||||
};
|
||||
let xml = unsafe { from_utf8_unchecked(&bytes) };
|
||||
|
||||
Ok(xml.to_string())
|
||||
},
|
||||
_ => Err(ReportError::UnsupportedFormat)
|
||||
}
|
||||
_ => Err(ReportError::UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+70
-88
@@ -2,15 +2,14 @@
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use rpki::uri;
|
||||
use rpki::crypto::Signer;
|
||||
use rpki::uri;
|
||||
|
||||
use crate::api::Link;
|
||||
use std::path::Path;
|
||||
use api::ca::ResourceSet;
|
||||
use remote::rfc8183;
|
||||
use remote::rfc8183::{ChildRequest, ServiceUri};
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
//------------ Handle --------------------------------------------------------
|
||||
|
||||
@@ -59,7 +58,6 @@ impl fmt::Display for Handle {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Token ------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
@@ -98,24 +96,19 @@ impl fmt::Display for Token {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherRequest ----------------------------------------------
|
||||
|
||||
/// This type defines request for a new Publisher (CA that is allowed to
|
||||
/// publish).
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PublisherRequest {
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
base_uri: uri::Rsync,
|
||||
}
|
||||
|
||||
impl PublisherRequest {
|
||||
pub fn new(
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
base_uri: uri::Rsync,
|
||||
) -> Self {
|
||||
pub fn new(handle: Handle, token: Token, base_uri: uri::Rsync) -> Self {
|
||||
PublisherRequest {
|
||||
handle,
|
||||
token,
|
||||
@@ -145,14 +138,12 @@ impl PublisherRequest {
|
||||
|
||||
impl PartialEq for PublisherRequest {
|
||||
fn eq(&self, other: &PublisherRequest) -> bool {
|
||||
self.handle == other.handle &&
|
||||
self.base_uri == other.base_uri
|
||||
self.handle == other.handle && self.base_uri == other.base_uri
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PublisherRequest {}
|
||||
|
||||
|
||||
//------------ PublisherSummaryInfo ------------------------------------------
|
||||
|
||||
/// Defines a summary of publisher information to be used in the publisher
|
||||
@@ -160,51 +151,45 @@ impl Eq for PublisherRequest {}
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct PublisherSummary {
|
||||
id: String,
|
||||
links: Vec<Link>
|
||||
links: Vec<Link>,
|
||||
}
|
||||
|
||||
impl PublisherSummary {
|
||||
pub fn from(
|
||||
handle: &Handle,
|
||||
path_publishers: &str
|
||||
) -> PublisherSummary {
|
||||
pub fn from(handle: &Handle, path_publishers: &str) -> PublisherSummary {
|
||||
let mut links = Vec::new();
|
||||
let self_link = Link {
|
||||
rel: "self".to_string(),
|
||||
link: format!("{}/{}", path_publishers, handle)
|
||||
link: format!("{}/{}", path_publishers, handle),
|
||||
};
|
||||
links.push(self_link);
|
||||
|
||||
PublisherSummary {
|
||||
id: handle.to_string(),
|
||||
links
|
||||
links,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str { &self.id }
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherList -------------------------------------------------
|
||||
|
||||
/// This type represents a list of (all) current publishers to show in the API
|
||||
#[derive(Clone, Eq, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct PublisherList {
|
||||
publishers: Vec<PublisherSummary>
|
||||
publishers: Vec<PublisherSummary>,
|
||||
}
|
||||
|
||||
impl PublisherList {
|
||||
pub fn build(
|
||||
publishers: &[Handle],
|
||||
path_publishers: &str
|
||||
) -> PublisherList {
|
||||
let publishers: Vec<PublisherSummary> = publishers.iter().map(|p|
|
||||
PublisherSummary::from(&p, path_publishers)
|
||||
).collect();
|
||||
pub fn build(publishers: &[Handle], path_publishers: &str) -> PublisherList {
|
||||
let publishers: Vec<PublisherSummary> = publishers
|
||||
.iter()
|
||||
.map(|p| PublisherSummary::from(&p, path_publishers))
|
||||
.collect();
|
||||
|
||||
PublisherList {
|
||||
publishers
|
||||
}
|
||||
PublisherList { publishers }
|
||||
}
|
||||
|
||||
pub fn publishers(&self) -> &Vec<PublisherSummary> {
|
||||
@@ -212,7 +197,6 @@ impl PublisherList {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherDetails ----------------------------------------------
|
||||
|
||||
/// This type defines the publisher details for:
|
||||
@@ -229,57 +213,64 @@ impl PublisherDetails {
|
||||
PublisherDetails {
|
||||
handle: handle.to_string(),
|
||||
deactivated,
|
||||
base_uri: base_uri.clone()
|
||||
base_uri: base_uri.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> &str { &self.handle }
|
||||
pub fn deactivated(&self) -> bool { self.deactivated }
|
||||
pub fn base_uri(&self) -> &uri::Rsync { &self.base_uri }
|
||||
pub fn handle(&self) -> &str {
|
||||
&self.handle
|
||||
}
|
||||
pub fn deactivated(&self) -> bool {
|
||||
self.deactivated
|
||||
}
|
||||
pub fn base_uri(&self) -> &uri::Rsync {
|
||||
&self.base_uri
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PublisherDetails {
|
||||
fn eq(&self, other: &PublisherDetails) -> bool {
|
||||
match (serde_json::to_string(self), serde_json::to_string(other)) {
|
||||
(Ok(ser_self), Ok(ser_other)) => ser_self == ser_other,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PublisherDetails {}
|
||||
|
||||
|
||||
|
||||
//------------ PublisherClientRequest ----------------------------------------
|
||||
|
||||
/// This type defines request for a new Publisher client, i.e. the proxy that
|
||||
/// is used by an embedded CA to do the actual publication.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PublisherClientRequest {
|
||||
handle: Handle,
|
||||
server_info: PubServerContact
|
||||
handle: Handle,
|
||||
server_info: PubServerContact,
|
||||
}
|
||||
|
||||
impl PublisherClientRequest {
|
||||
pub fn new(handle: Handle, server_info: PubServerContact) -> Self {
|
||||
PublisherClientRequest { handle, server_info }
|
||||
PublisherClientRequest {
|
||||
handle,
|
||||
server_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn embedded(
|
||||
handle: Handle
|
||||
) -> Self {
|
||||
pub fn embedded(handle: Handle) -> Self {
|
||||
let server_info = PubServerContact::embedded();
|
||||
PublisherClientRequest { handle, server_info }
|
||||
PublisherClientRequest {
|
||||
handle,
|
||||
server_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn krill(
|
||||
handle: Handle,
|
||||
service_uri: uri::Https,
|
||||
token: Token
|
||||
) -> Self {
|
||||
pub fn krill(handle: Handle, service_uri: uri::Https, token: Token) -> Self {
|
||||
let server_info = PubServerContact::for_krill(service_uri, token);
|
||||
PublisherClientRequest { handle, server_info }
|
||||
PublisherClientRequest {
|
||||
handle,
|
||||
server_info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Handle, PubServerContact) {
|
||||
@@ -287,7 +278,6 @@ impl PublisherClientRequest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PubServerInfo -------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Display, Serialize)]
|
||||
@@ -296,7 +286,7 @@ pub enum PubServerContact {
|
||||
Embedded,
|
||||
|
||||
#[display(fmt = "Remote Krill at: {}, using token: {}", _0, _1)]
|
||||
KrillServer(uri::Https, Token)
|
||||
KrillServer(uri::Https, Token),
|
||||
}
|
||||
|
||||
impl PubServerContact {
|
||||
@@ -309,21 +299,17 @@ impl PubServerContact {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ParentCaReq ---------------------------------------------------
|
||||
|
||||
/// This type defines all parent ca details needed to add a parent to a CA
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct AddParentRequest {
|
||||
handle: Handle, // the local name the child gave to the parent
|
||||
contact: ParentCaContact // where the parent can be contacted
|
||||
contact: ParentCaContact, // where the parent can be contacted
|
||||
}
|
||||
|
||||
impl AddParentRequest {
|
||||
pub fn new(
|
||||
handle: Handle,
|
||||
contact: ParentCaContact
|
||||
) -> Self {
|
||||
pub fn new(handle: Handle, contact: ParentCaContact) -> Self {
|
||||
AddParentRequest { handle, contact }
|
||||
}
|
||||
|
||||
@@ -346,7 +332,7 @@ pub enum ParentCaContact {
|
||||
Embedded(Handle, Token),
|
||||
|
||||
#[display(fmt = "RFC 6492 Parent")]
|
||||
Rfc6492(rfc8183::ParentResponse)
|
||||
Rfc6492(rfc8183::ParentResponse),
|
||||
}
|
||||
|
||||
impl ParentCaContact {
|
||||
@@ -363,52 +349,50 @@ impl ParentCaContact {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CertAuthInit --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct CertAuthInit {
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
pub_mode: CertAuthPubMode
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
pub_mode: CertAuthPubMode,
|
||||
}
|
||||
|
||||
impl CertAuthInit {
|
||||
pub fn new(
|
||||
handle: Handle,
|
||||
token: Token,
|
||||
pub_mode: CertAuthPubMode
|
||||
) -> Self {
|
||||
CertAuthInit { handle, token, pub_mode }
|
||||
pub fn new(handle: Handle, token: Token, pub_mode: CertAuthPubMode) -> Self {
|
||||
CertAuthInit {
|
||||
handle,
|
||||
token,
|
||||
pub_mode,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Handle, Token, CertAuthPubMode) {
|
||||
( self.handle, self.token, self.pub_mode )
|
||||
(self.handle, self.token, self.pub_mode)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum CertAuthPubMode {
|
||||
Embedded
|
||||
Embedded,
|
||||
}
|
||||
|
||||
|
||||
//------------ AddChildRequest -----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct AddChildRequest {
|
||||
handle: Handle,
|
||||
resources: ResourceSet,
|
||||
auth: ChildAuthRequest
|
||||
auth: ChildAuthRequest,
|
||||
}
|
||||
|
||||
impl AddChildRequest {
|
||||
pub fn new(
|
||||
handle: Handle,
|
||||
resources: ResourceSet,
|
||||
auth: ChildAuthRequest
|
||||
) -> Self {
|
||||
AddChildRequest { handle, resources, auth }
|
||||
pub fn new(handle: Handle, resources: ResourceSet, auth: ChildAuthRequest) -> Self {
|
||||
AddChildRequest {
|
||||
handle,
|
||||
resources,
|
||||
auth,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Handle, ResourceSet, ChildAuthRequest) {
|
||||
@@ -421,7 +405,5 @@ impl AddChildRequest {
|
||||
pub enum ChildAuthRequest {
|
||||
Embedded(Token),
|
||||
Remote(Token),
|
||||
Rfc8183(ChildRequest)
|
||||
Rfc8183(ChildRequest),
|
||||
}
|
||||
|
||||
|
||||
|
||||
+284
-278
File diff suppressed because it is too large
Load Diff
+56
-59
@@ -20,7 +20,6 @@ use rpki::manifest::Manifest;
|
||||
|
||||
use crate::util::sha256;
|
||||
|
||||
|
||||
//------------ Base64 --------------------------------------------------------
|
||||
|
||||
/// This type contains a base64 encoded structure. The publication protocol
|
||||
@@ -82,30 +81,29 @@ impl From<&Crl> for Base64 {
|
||||
|
||||
impl ToString for Base64 {
|
||||
fn to_string(&self) -> String {
|
||||
unsafe {
|
||||
String::from_utf8_unchecked(self.0.to_vec())
|
||||
}
|
||||
unsafe { String::from_utf8_unchecked(self.0.to_vec()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Base64 {
|
||||
fn serialize<S>(
|
||||
&self, serializer: S
|
||||
) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Base64 {
|
||||
fn deserialize<D>(
|
||||
deserializer: D
|
||||
) -> Result<Base64, D::Error> where D: Deserializer<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Base64, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
Ok(Base64::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ EncodedHash ---------------------------------------------------
|
||||
|
||||
/// This type contains a hex encoded sha256 hash.
|
||||
@@ -143,31 +141,29 @@ impl From<String> for EncodedHash {
|
||||
|
||||
impl ToString for EncodedHash {
|
||||
fn to_string(&self) -> String {
|
||||
unsafe {
|
||||
String::from_utf8_unchecked(self.0.to_vec())
|
||||
}
|
||||
unsafe { String::from_utf8_unchecked(self.0.to_vec()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for EncodedHash {
|
||||
fn serialize<S>(
|
||||
&self, serializer: S
|
||||
) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for EncodedHash {
|
||||
fn deserialize<D>(
|
||||
deserializer: D
|
||||
) -> Result<EncodedHash, D::Error> where D: Deserializer<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<EncodedHash, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
Ok(EncodedHash::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ Link ----------------------------------------------------------
|
||||
|
||||
/// Defines a link element to include as part of a links array in a Json
|
||||
@@ -175,10 +171,9 @@ impl<'de> Deserialize<'de> for EncodedHash {
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Link {
|
||||
rel: String,
|
||||
link: String
|
||||
link: String,
|
||||
}
|
||||
|
||||
|
||||
//------------ ErrorResponse --------------------------------------------------
|
||||
|
||||
/// Defines an error response. Codes are unique and documented here:
|
||||
@@ -186,13 +181,19 @@ pub struct Link {
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ErrorResponse {
|
||||
code: usize,
|
||||
msg: String
|
||||
msg: String,
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
pub fn new(code: usize, msg: String) -> Self { ErrorResponse { code, msg }}
|
||||
pub fn code(&self) -> usize { self.code }
|
||||
pub fn msg(&self) -> &str { &self.msg }
|
||||
pub fn new(code: usize, msg: String) -> Self {
|
||||
ErrorResponse { code, msg }
|
||||
}
|
||||
pub fn code(&self) -> usize {
|
||||
self.code
|
||||
}
|
||||
pub fn msg(&self) -> &str {
|
||||
&self.msg
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorResponse {
|
||||
@@ -210,79 +211,79 @@ impl Into<ErrorCode> for ErrorResponse {
|
||||
/// This type defines externally visible errors that the API may return.
|
||||
#[derive(Clone, Debug, Display, Eq, PartialEq)]
|
||||
pub enum ErrorCode {
|
||||
#[display(fmt="Submitted Json cannot be parsed")]
|
||||
#[display(fmt = "Submitted Json cannot be parsed")]
|
||||
InvalidJson,
|
||||
|
||||
#[display(fmt="Invalid RFC8183 Publisher Request")]
|
||||
#[display(fmt = "Invalid RFC8183 Publisher Request")]
|
||||
InvalidPublisherRequest,
|
||||
|
||||
#[display(fmt="Issue with submitted publication XML")]
|
||||
#[display(fmt = "Issue with submitted publication XML")]
|
||||
InvalidPublicationXml,
|
||||
|
||||
#[display(fmt="Invalid handle name")]
|
||||
#[display(fmt = "Invalid handle name")]
|
||||
InvalidHandle,
|
||||
|
||||
#[display(fmt="Submitted protocol CMS cannot be parsed")]
|
||||
#[display(fmt = "Submitted protocol CMS cannot be parsed")]
|
||||
InvalidCms,
|
||||
|
||||
#[display(fmt="2001: Submitted protocol CMS does not validate")]
|
||||
#[display(fmt = "2001: Submitted protocol CMS does not validate")]
|
||||
CmsValidation,
|
||||
|
||||
#[display(fmt="Out of sync with server, please send requests for instances sequentially")]
|
||||
#[display(fmt = "Out of sync with server, please send requests for instances sequentially")]
|
||||
ConcurrentModification,
|
||||
|
||||
#[display(fmt="Unknown publisher")]
|
||||
#[display(fmt = "Unknown publisher")]
|
||||
UnknownPublisher,
|
||||
|
||||
#[display(fmt="Handle already in use")]
|
||||
#[display(fmt = "Handle already in use")]
|
||||
DuplicateHandle,
|
||||
|
||||
#[display(fmt="Base URI for publisher is outside of publisher base URI")]
|
||||
#[display(fmt = "Base URI for publisher is outside of publisher base URI")]
|
||||
InvalidBaseUri,
|
||||
|
||||
#[display(fmt="Not allowed to publish outside of publisher jail")]
|
||||
#[display(fmt = "Not allowed to publish outside of publisher jail")]
|
||||
UriOutsideJail,
|
||||
|
||||
#[display(fmt="File already exists for uri (use update!)")]
|
||||
#[display(fmt = "File already exists for uri (use update!)")]
|
||||
ObjectAlreadyPresent,
|
||||
|
||||
#[display(fmt="No file found for hash at uri")]
|
||||
#[display(fmt = "No file found for hash at uri")]
|
||||
NoObjectForHashAndOrUri,
|
||||
|
||||
#[display(fmt="Publisher has been deactivated")]
|
||||
#[display(fmt = "Publisher has been deactivated")]
|
||||
PublisherDeactivated,
|
||||
|
||||
// 2300s CA Admin Issues
|
||||
#[display(fmt="Child with name exists")]
|
||||
#[display(fmt = "Child with name exists")]
|
||||
DuplicateChild,
|
||||
|
||||
#[display(fmt="Child MUST have resources")]
|
||||
#[display(fmt = "Child MUST have resources")]
|
||||
ChildNeedsResources,
|
||||
|
||||
#[display(fmt="Child cannot have resources not held by parent")]
|
||||
#[display(fmt = "Child cannot have resources not held by parent")]
|
||||
ChildOverclaims,
|
||||
|
||||
// 3000s General server errors
|
||||
#[display(fmt="Cannot update internal state, issue with work_dir?")]
|
||||
#[display(fmt = "Cannot update internal state, issue with work_dir?")]
|
||||
Persistence,
|
||||
|
||||
#[display(fmt="Cannot update repository, issue with repo_dir?")]
|
||||
#[display(fmt = "Cannot update repository, issue with repo_dir?")]
|
||||
RepositoryUpdate,
|
||||
|
||||
#[display(fmt="Signing error, issue with openssl version or work_dir?")]
|
||||
#[display(fmt = "Signing error, issue with openssl version or work_dir?")]
|
||||
SigningError,
|
||||
|
||||
#[display(fmt="Proxy server error.")]
|
||||
#[display(fmt = "Proxy server error.")]
|
||||
ProxyError,
|
||||
|
||||
#[display(fmt="General CA Server issue.")]
|
||||
#[display(fmt = "General CA Server issue.")]
|
||||
CaServerError,
|
||||
|
||||
#[display(fmt="Publication Client Server issue.")]
|
||||
#[display(fmt = "Publication Client Server issue.")]
|
||||
PubClientServerError,
|
||||
|
||||
#[display(fmt="Unrecognised error (this is a bug)")]
|
||||
Unknown
|
||||
#[display(fmt = "Unrecognised error (this is a bug)")]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<usize> for ErrorCode {
|
||||
@@ -323,7 +324,7 @@ impl From<usize> for ErrorCode {
|
||||
3005 => ErrorCode::CaServerError,
|
||||
3006 => ErrorCode::PubClientServerError,
|
||||
|
||||
_ => ErrorCode::Unknown
|
||||
_ => ErrorCode::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,7 +367,7 @@ impl Into<ErrorResponse> for ErrorCode {
|
||||
ErrorCode::CaServerError => 3005,
|
||||
ErrorCode::PubClientServerError => 3006,
|
||||
|
||||
ErrorCode::Unknown => 65535
|
||||
ErrorCode::Unknown => 65535,
|
||||
};
|
||||
let msg = format!("{}", self);
|
||||
|
||||
@@ -382,7 +383,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_convert_code_to_number_and_back() {
|
||||
|
||||
fn test_code(number_to_test: usize) {
|
||||
let code = ErrorCode::from(number_to_test);
|
||||
let response: ErrorResponse = code.into();
|
||||
@@ -412,8 +412,5 @@ mod tests {
|
||||
for n in 3001..3007 {
|
||||
test_code(n)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+151
-101
@@ -1,10 +1,10 @@
|
||||
use api::ca::{ResourceSet, IssuedCert, RcvdCert};
|
||||
use rpki::x509::Time;
|
||||
use api::ca::{IssuedCert, RcvdCert, ResourceSet};
|
||||
use rpki::cert::{Cert, Overclaim};
|
||||
use rpki::crypto::{KeyIdentifier, PublicKey};
|
||||
use rpki::csr::Csr;
|
||||
use rpki::uri;
|
||||
use rpki::resources::{AsResources, Ipv4Resources, Ipv6Resources};
|
||||
use rpki::crypto::{PublicKey, KeyIdentifier};
|
||||
use rpki::uri;
|
||||
use rpki::x509::Time;
|
||||
|
||||
pub const DFLT_CLASS: &str = "all";
|
||||
|
||||
@@ -14,30 +14,32 @@ pub const DFLT_CLASS: &str = "all";
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum ProvisioningRequest {
|
||||
List,
|
||||
Request(IssuanceRequest)
|
||||
Request(IssuanceRequest),
|
||||
}
|
||||
|
||||
impl ProvisioningRequest {
|
||||
pub fn list() -> Self { ProvisioningRequest::List }
|
||||
pub fn request(r: IssuanceRequest) -> Self { ProvisioningRequest::Request(r)}
|
||||
pub fn list() -> Self {
|
||||
ProvisioningRequest::List
|
||||
}
|
||||
pub fn request(r: IssuanceRequest) -> Self {
|
||||
ProvisioningRequest::Request(r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ProvisioningResponse -----------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum ProvisioningResponse {
|
||||
List(Entitlements)
|
||||
List(Entitlements),
|
||||
}
|
||||
|
||||
|
||||
//------------ Entitlements -------------------------------------------------
|
||||
|
||||
/// This structure is what is called the "Resource Class List Response"
|
||||
/// in section 3.3.2 of RFC6492.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Entitlements {
|
||||
classes: Vec<EntitlementClass>
|
||||
classes: Vec<EntitlementClass>,
|
||||
}
|
||||
|
||||
impl Entitlements {
|
||||
@@ -45,21 +47,28 @@ impl Entitlements {
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet,
|
||||
not_after: Time,
|
||||
issued: Vec<IssuedCert>
|
||||
issued: Vec<IssuedCert>,
|
||||
) -> Self {
|
||||
let name = DFLT_CLASS.to_string();
|
||||
Entitlements { classes: vec![
|
||||
EntitlementClass { class_name: name, issuer, resource_set, not_after, issued }
|
||||
]}
|
||||
Entitlements {
|
||||
classes: vec![EntitlementClass {
|
||||
class_name: name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued,
|
||||
}],
|
||||
}
|
||||
}
|
||||
pub fn new(classes: Vec<EntitlementClass>) -> Self {
|
||||
Entitlements { classes }
|
||||
}
|
||||
|
||||
pub fn classes(&self) -> &Vec<EntitlementClass> { &self.classes }
|
||||
pub fn classes(&self) -> &Vec<EntitlementClass> {
|
||||
&self.classes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ EntitlementClass ----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
@@ -68,7 +77,7 @@ pub struct EntitlementClass {
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet,
|
||||
not_after: Time,
|
||||
issued: Vec<IssuedCert>
|
||||
issued: Vec<IssuedCert>,
|
||||
}
|
||||
|
||||
impl EntitlementClass {
|
||||
@@ -77,27 +86,41 @@ impl EntitlementClass {
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet,
|
||||
not_after: Time,
|
||||
issued: Vec<IssuedCert>
|
||||
issued: Vec<IssuedCert>,
|
||||
) -> Self {
|
||||
EntitlementClass { class_name, issuer, resource_set, not_after, issued }
|
||||
EntitlementClass {
|
||||
class_name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap(
|
||||
self
|
||||
) -> (String, SigningCert, ResourceSet, Time, Vec<IssuedCert>) {
|
||||
fn unwrap(self) -> (String, SigningCert, ResourceSet, Time, Vec<IssuedCert>) {
|
||||
(
|
||||
self.class_name,
|
||||
self.issuer,
|
||||
self.resource_set,
|
||||
self.not_after,
|
||||
self.issued
|
||||
self.issued,
|
||||
)
|
||||
}
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn issuer(&self) -> &SigningCert { &self.issuer }
|
||||
pub fn resource_set(&self) -> &ResourceSet { &self.resource_set }
|
||||
pub fn not_after(&self) -> Time { self.not_after }
|
||||
pub fn issued(&self) -> &Vec<IssuedCert> { &self.issued }
|
||||
pub fn class_name(&self) -> &str {
|
||||
&self.class_name
|
||||
}
|
||||
pub fn issuer(&self) -> &SigningCert {
|
||||
&self.issuer
|
||||
}
|
||||
pub fn resource_set(&self) -> &ResourceSet {
|
||||
&self.resource_set
|
||||
}
|
||||
pub fn not_after(&self) -> Time {
|
||||
self.not_after
|
||||
}
|
||||
pub fn issued(&self) -> &Vec<IssuedCert> {
|
||||
&self.issued
|
||||
}
|
||||
|
||||
/// Converts this into an IssuanceResponse for the given key. I.e. includes
|
||||
/// the issued certificate matching the given public key only. Returns a
|
||||
@@ -105,27 +128,21 @@ impl EntitlementClass {
|
||||
pub fn into_issuance_response(self, key: &PublicKey) -> Option<IssuanceResponse> {
|
||||
let (class_name, issuer, resource_set, not_after, issued) = self.unwrap();
|
||||
|
||||
issued.into_iter()
|
||||
issued
|
||||
.into_iter()
|
||||
.find(|issued| issued.cert().subject_public_key_info() == key)
|
||||
.map(|issued| {
|
||||
IssuanceResponse::new(
|
||||
class_name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued
|
||||
)
|
||||
IssuanceResponse::new(class_name, issuer, resource_set, not_after, issued)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ SigningCert ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct SigningCert {
|
||||
uri: uri::Rsync,
|
||||
cert: Cert
|
||||
cert: Cert,
|
||||
}
|
||||
|
||||
impl SigningCert {
|
||||
@@ -133,15 +150,18 @@ impl SigningCert {
|
||||
SigningCert { uri, cert }
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri }
|
||||
pub fn cert(&self) -> &Cert { &self.cert }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn cert(&self) -> &Cert {
|
||||
&self.cert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl PartialEq for SigningCert {
|
||||
fn eq(&self, other: &SigningCert) -> bool {
|
||||
self.uri == other.uri &&
|
||||
self.cert.to_captured().as_slice() == other.cert.to_captured().as_slice()
|
||||
self.uri == other.uri
|
||||
&& self.cert.to_captured().as_slice() == other.cert.to_captured().as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +171,11 @@ impl From<&RcvdCert> for SigningCert {
|
||||
fn from(c: &RcvdCert) -> Self {
|
||||
SigningCert {
|
||||
uri: c.uri().clone(),
|
||||
cert: c.cert().clone()
|
||||
cert: c.cert().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ IssuanceRequest -----------------------------------------------
|
||||
|
||||
/// This type reflects the content of a Certificate Issuance Request
|
||||
@@ -165,38 +184,43 @@ impl From<&RcvdCert> for SigningCert {
|
||||
pub struct IssuanceRequest {
|
||||
class_name: String,
|
||||
limit: RequestResourceLimit,
|
||||
csr: Csr
|
||||
csr: Csr,
|
||||
}
|
||||
|
||||
impl IssuanceRequest {
|
||||
pub fn new(
|
||||
class_name: String,
|
||||
limit: RequestResourceLimit,
|
||||
csr: Csr
|
||||
) -> Self {
|
||||
IssuanceRequest { class_name, limit, csr }
|
||||
pub fn new(class_name: String, limit: RequestResourceLimit, csr: Csr) -> Self {
|
||||
IssuanceRequest {
|
||||
class_name,
|
||||
limit,
|
||||
csr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (String, RequestResourceLimit, Csr) {
|
||||
(self.class_name, self.limit, self.csr)
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn limit(&self) -> &RequestResourceLimit { &self.limit }
|
||||
pub fn csr(&self) -> &Csr { &self.csr }
|
||||
pub fn class_name(&self) -> &str {
|
||||
&self.class_name
|
||||
}
|
||||
pub fn limit(&self) -> &RequestResourceLimit {
|
||||
&self.limit
|
||||
}
|
||||
pub fn csr(&self) -> &Csr {
|
||||
&self.csr
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for IssuanceRequest {
|
||||
fn eq(&self, other: &IssuanceRequest) -> bool {
|
||||
self.class_name == other.class_name &&
|
||||
self.limit == other.limit &&
|
||||
self.csr.to_captured().as_slice() == other.csr.to_captured().as_slice()
|
||||
self.class_name == other.class_name
|
||||
&& self.limit == other.limit
|
||||
&& self.csr.to_captured().as_slice() == other.csr.to_captured().as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for IssuanceRequest {}
|
||||
|
||||
|
||||
//------------ IssuanceResponse ----------------------------------------------
|
||||
|
||||
/// A Certificate Issuance Response equivalent to the one defined in
|
||||
@@ -210,7 +234,7 @@ pub struct IssuanceResponse {
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet, // resources allowed on a cert
|
||||
not_after: Time,
|
||||
issued: IssuedCert
|
||||
issued: IssuedCert,
|
||||
}
|
||||
|
||||
impl IssuanceResponse {
|
||||
@@ -219,23 +243,38 @@ impl IssuanceResponse {
|
||||
issuer: SigningCert,
|
||||
resource_set: ResourceSet, // resources allowed on a cert
|
||||
not_after: Time,
|
||||
issued: IssuedCert
|
||||
issued: IssuedCert,
|
||||
) -> Self {
|
||||
IssuanceResponse { class_name, issuer, resource_set, not_after, issued }
|
||||
IssuanceResponse {
|
||||
class_name,
|
||||
issuer,
|
||||
resource_set,
|
||||
not_after,
|
||||
issued,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (String, SigningCert, ResourceSet, IssuedCert) {
|
||||
(self.class_name, self.issuer, self.resource_set, self.issued)
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn issuer(&self) -> &SigningCert { &self.issuer }
|
||||
pub fn resource_set(&self) -> &ResourceSet { &self.resource_set }
|
||||
pub fn not_after(&self) -> Time { self.not_after }
|
||||
pub fn issued(&self) -> &IssuedCert { &self.issued }
|
||||
pub fn class_name(&self) -> &str {
|
||||
&self.class_name
|
||||
}
|
||||
pub fn issuer(&self) -> &SigningCert {
|
||||
&self.issuer
|
||||
}
|
||||
pub fn resource_set(&self) -> &ResourceSet {
|
||||
&self.resource_set
|
||||
}
|
||||
pub fn not_after(&self) -> Time {
|
||||
self.not_after
|
||||
}
|
||||
pub fn issued(&self) -> &IssuedCert {
|
||||
&self.issued
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RequestResourceLimit ------------------------------------------
|
||||
|
||||
/// The scope of resources that a child CA wants to have certified. By default
|
||||
@@ -250,11 +289,13 @@ impl IssuanceResponse {
|
||||
pub struct RequestResourceLimit {
|
||||
asn: Option<AsResources>,
|
||||
v4: Option<Ipv4Resources>,
|
||||
v6: Option<Ipv6Resources>
|
||||
v6: Option<Ipv6Resources>,
|
||||
}
|
||||
|
||||
impl RequestResourceLimit {
|
||||
pub fn new() -> RequestResourceLimit { Self::default() }
|
||||
pub fn new() -> RequestResourceLimit {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.asn == None && self.v4 == None && self.v6 == None
|
||||
@@ -272,9 +313,15 @@ impl RequestResourceLimit {
|
||||
self.v6 = Some(ipv6);
|
||||
}
|
||||
|
||||
pub fn asn(&self) -> Option<&AsResources> { self.asn.as_ref() }
|
||||
pub fn v4(&self) -> Option<&Ipv4Resources> { self.v4.as_ref() }
|
||||
pub fn v6(&self) -> Option<&Ipv6Resources> { self.v6.as_ref() }
|
||||
pub fn asn(&self) -> Option<&AsResources> {
|
||||
self.asn.as_ref()
|
||||
}
|
||||
pub fn v4(&self) -> Option<&Ipv4Resources> {
|
||||
self.v4.as_ref()
|
||||
}
|
||||
pub fn v6(&self) -> Option<&Ipv6Resources> {
|
||||
self.v6.as_ref()
|
||||
}
|
||||
|
||||
/// Give back a ResourceSet based on the input set as limited by this.
|
||||
/// Note, if the limit exceeds the input set for any resource type
|
||||
@@ -289,14 +336,14 @@ impl RequestResourceLimit {
|
||||
// resources. This is unverifiable. As Krill
|
||||
// will never use the "inherit" type on CA certificates
|
||||
// it is safe to just return a None here.
|
||||
return None
|
||||
},
|
||||
return None;
|
||||
}
|
||||
Some(parent_asn) => {
|
||||
if parent_asn.validate_issued(
|
||||
Some(asn),
|
||||
Overclaim::Refuse
|
||||
).is_err() {
|
||||
return None // Child is overclaiming
|
||||
if parent_asn
|
||||
.validate_issued(Some(asn), Overclaim::Refuse)
|
||||
.is_err()
|
||||
{
|
||||
return None; // Child is overclaiming
|
||||
}
|
||||
asn.clone() // Child gets what they ask for
|
||||
}
|
||||
@@ -313,14 +360,14 @@ impl RequestResourceLimit {
|
||||
// resources. This is unverifiable. As Krill
|
||||
// will never use the "inherit" type on CA certificates
|
||||
// it is safe to just return a None here.
|
||||
return None
|
||||
},
|
||||
return None;
|
||||
}
|
||||
Some(parent_v4) => {
|
||||
if parent_v4.validate_issued(
|
||||
Some(v4),
|
||||
Overclaim::Refuse
|
||||
).is_err() {
|
||||
return None // Child is overclaiming
|
||||
if parent_v4
|
||||
.validate_issued(Some(v4), Overclaim::Refuse)
|
||||
.is_err()
|
||||
{
|
||||
return None; // Child is overclaiming
|
||||
}
|
||||
v4.clone() // Child gets what they ask for
|
||||
}
|
||||
@@ -337,14 +384,14 @@ impl RequestResourceLimit {
|
||||
// resources. This is unverifiable. As Krill
|
||||
// will never use the "inherit" type on CA certificates
|
||||
// it is safe to just return a None here.
|
||||
return None
|
||||
},
|
||||
return None;
|
||||
}
|
||||
Some(parent_v6) => {
|
||||
if parent_v6.validate_issued(
|
||||
Some(v6),
|
||||
Overclaim::Refuse
|
||||
).is_err() {
|
||||
return None // Child is overclaiming
|
||||
if parent_v6
|
||||
.validate_issued(Some(v6), Overclaim::Refuse)
|
||||
.is_err()
|
||||
{
|
||||
return None; // Child is overclaiming
|
||||
}
|
||||
v6.clone() // Child gets what they ask for
|
||||
}
|
||||
@@ -361,12 +408,11 @@ impl Default for RequestResourceLimit {
|
||||
RequestResourceLimit {
|
||||
asn: None,
|
||||
v4: None,
|
||||
v6: None
|
||||
v6: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RevocationRequest ---------------------------------------------
|
||||
|
||||
/// This type represents a Certificate Revocation Request as
|
||||
@@ -374,14 +420,18 @@ impl Default for RequestResourceLimit {
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct RevocationRequest {
|
||||
class_name: String,
|
||||
key: KeyIdentifier
|
||||
key: KeyIdentifier,
|
||||
}
|
||||
|
||||
impl RevocationRequest {
|
||||
pub fn new(class_name: String, key: KeyIdentifier) -> Self {
|
||||
RevocationRequest { class_name, key}
|
||||
RevocationRequest { class_name, key }
|
||||
}
|
||||
|
||||
pub fn class_name(&self) -> &str { &self.class_name }
|
||||
pub fn key(&self) -> &KeyIdentifier { &self.key }
|
||||
pub fn class_name(&self) -> &str {
|
||||
&self.class_name
|
||||
}
|
||||
pub fn key(&self) -> &KeyIdentifier {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! Support for requests sent to the Json API
|
||||
use rpki::uri;
|
||||
use crate::api::{ Base64, EncodedHash };
|
||||
use crate::api::{Base64, EncodedHash};
|
||||
use crate::util::file::CurrentFile;
|
||||
|
||||
use rpki::uri;
|
||||
|
||||
//------------ PublishRequest ------------------------------------------------
|
||||
|
||||
@@ -11,10 +10,9 @@ use crate::util::file::CurrentFile;
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum PublishRequest {
|
||||
List, // See https://tools.ietf.org/html/rfc8181#section-2.3
|
||||
Delta(PublishDelta)
|
||||
Delta(PublishDelta),
|
||||
}
|
||||
|
||||
|
||||
//------------ PublishDelta ------------------------------------------------
|
||||
|
||||
/// This type represents a multi element query as described in
|
||||
@@ -23,16 +21,16 @@ pub enum PublishRequest {
|
||||
pub struct PublishDelta {
|
||||
publishes: Vec<Publish>,
|
||||
updates: Vec<Update>,
|
||||
withdraws: Vec<Withdraw>
|
||||
withdraws: Vec<Withdraw>,
|
||||
}
|
||||
|
||||
impl PublishDelta {
|
||||
pub fn new(
|
||||
publishes: Vec<Publish>,
|
||||
updates: Vec<Update>,
|
||||
withdraws: Vec<Withdraw>
|
||||
) -> Self {
|
||||
PublishDelta { publishes, updates, withdraws }
|
||||
pub fn new(publishes: Vec<Publish>, updates: Vec<Update>, withdraws: Vec<Withdraw>) -> Self {
|
||||
PublishDelta {
|
||||
publishes,
|
||||
updates,
|
||||
withdraws,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn publishes(&self) -> &Vec<Publish> {
|
||||
@@ -49,21 +47,22 @@ impl PublishDelta {
|
||||
self.publishes.len() + self.updates.len() + self.withdraws.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Vec<Publish>, Vec<Update>, Vec<Withdraw>) {
|
||||
(self.publishes, self.updates, self.withdraws)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublishDeltaBuilder -------------------------------------------
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PublishDeltaBuilder {
|
||||
publishes: Vec<Publish>,
|
||||
updates: Vec<Update>,
|
||||
withdraws: Vec<Withdraw>
|
||||
withdraws: Vec<Withdraw>,
|
||||
}
|
||||
|
||||
impl PublishDeltaBuilder {
|
||||
@@ -87,12 +86,11 @@ impl PublishDeltaBuilder {
|
||||
PublishDelta {
|
||||
publishes: self.publishes,
|
||||
updates: self.updates,
|
||||
withdraws: self.withdraws
|
||||
withdraws: self.withdraws,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Publish ------------------------------------------------------
|
||||
|
||||
/// Type representing a json equivalent to the publish element, that does not
|
||||
@@ -102,7 +100,7 @@ impl PublishDeltaBuilder {
|
||||
pub struct Publish {
|
||||
tag: Option<String>,
|
||||
uri: uri::Rsync,
|
||||
content: Base64
|
||||
content: Base64,
|
||||
}
|
||||
|
||||
impl Publish {
|
||||
@@ -114,22 +112,27 @@ impl Publish {
|
||||
Publish { tag, uri, content }
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> &Option<String> { &self.tag }
|
||||
pub fn tag(&self) -> &Option<String> {
|
||||
&self.tag
|
||||
}
|
||||
pub fn tag_for_xml(&self) -> String {
|
||||
match &self.tag {
|
||||
None => "".to_string(),
|
||||
Some(t) => t.clone()
|
||||
Some(t) => t.clone(),
|
||||
}
|
||||
}
|
||||
pub fn uri(&self) -> &uri::Rsync{ &self.uri}
|
||||
pub fn content(&self) -> &Base64{ &self.content }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn content(&self) -> &Base64 {
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Option<String>, uri::Rsync, Base64) {
|
||||
(self.tag, self.uri, self.content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Update --------------------------------------------------------
|
||||
|
||||
/// Type representing a json equivalent to the publish element, that updates
|
||||
@@ -148,36 +151,49 @@ impl Update {
|
||||
tag: Option<String>,
|
||||
uri: uri::Rsync,
|
||||
content: Base64,
|
||||
old_hash: EncodedHash
|
||||
old_hash: EncodedHash,
|
||||
) -> Self {
|
||||
Update { tag, uri, content, hash: old_hash }
|
||||
Update {
|
||||
tag,
|
||||
uri,
|
||||
content,
|
||||
hash: old_hash,
|
||||
}
|
||||
}
|
||||
pub fn with_hash_tag(
|
||||
uri: uri::Rsync,
|
||||
content: Base64,
|
||||
old_hash: EncodedHash
|
||||
) -> Self {
|
||||
pub fn with_hash_tag(uri: uri::Rsync, content: Base64, old_hash: EncodedHash) -> Self {
|
||||
let tag = Some(content.to_hex_hash());
|
||||
Update { tag, uri, content, hash: old_hash }
|
||||
Update {
|
||||
tag,
|
||||
uri,
|
||||
content,
|
||||
hash: old_hash,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> &Option<String> { &self.tag }
|
||||
pub fn tag(&self) -> &Option<String> {
|
||||
&self.tag
|
||||
}
|
||||
pub fn tag_for_xml(&self) -> String {
|
||||
match &self.tag {
|
||||
Some(t) => t.clone(),
|
||||
None => "".to_string()
|
||||
None => "".to_string(),
|
||||
}
|
||||
}
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri}
|
||||
pub fn content(&self) -> &Base64 { &self.content }
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn content(&self) -> &Base64 {
|
||||
&self.content
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Option<String>, uri::Rsync, Base64, EncodedHash) {
|
||||
(self.tag, self.uri, self.content, self.hash)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Withdraw ------------------------------------------------------
|
||||
|
||||
/// Type representing a json equivalent to a withdraw element that removes an
|
||||
@@ -204,19 +220,25 @@ impl Withdraw {
|
||||
Withdraw {
|
||||
tag: None,
|
||||
uri: el.uri().clone(),
|
||||
hash: el.hash().clone()
|
||||
hash: el.hash().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> &Option<String> { &self.tag }
|
||||
pub fn tag(&self) -> &Option<String> {
|
||||
&self.tag
|
||||
}
|
||||
pub fn tag_for_xml(&self) -> String {
|
||||
match &self.tag {
|
||||
Some(t) => t.clone(),
|
||||
None => "".to_string()
|
||||
None => "".to_string(),
|
||||
}
|
||||
}
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri}
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Option<String>, uri::Rsync, EncodedHash) {
|
||||
(self.tag, self.uri, self.hash)
|
||||
@@ -228,17 +250,16 @@ impl Withdraw {
|
||||
/// This type is used to wrap API responses for publication requests.
|
||||
pub enum PublishReply {
|
||||
Success, // See https://tools.ietf.org/html/rfc8181#section-3.4
|
||||
List(ListReply)
|
||||
List(ListReply),
|
||||
}
|
||||
|
||||
|
||||
//------------ ListReply -----------------------------------------------------
|
||||
|
||||
/// This type represents the list reply as described in
|
||||
/// https://tools.ietf.org/html/rfc8181#section-2.3
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct ListReply {
|
||||
elements: Vec<ListElement>
|
||||
elements: Vec<ListElement>,
|
||||
}
|
||||
|
||||
impl ListReply {
|
||||
@@ -247,7 +268,10 @@ impl ListReply {
|
||||
}
|
||||
|
||||
pub fn from_files(files: Vec<CurrentFile>) -> Self {
|
||||
let elements = files.into_iter().map(CurrentFile::into_list_element).collect();
|
||||
let elements = files
|
||||
.into_iter()
|
||||
.map(CurrentFile::into_list_element)
|
||||
.collect();
|
||||
ListReply { elements }
|
||||
}
|
||||
|
||||
@@ -256,15 +280,14 @@ impl ListReply {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ListElement ---------------------------------------------------
|
||||
|
||||
/// This type represents a single object that is published at a publication
|
||||
/// server.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub struct ListElement {
|
||||
uri: uri::Rsync,
|
||||
hash: EncodedHash
|
||||
uri: uri::Rsync,
|
||||
hash: EncodedHash,
|
||||
}
|
||||
|
||||
impl ListElement {
|
||||
@@ -272,6 +295,10 @@ impl ListElement {
|
||||
ListElement { uri, hash }
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri }
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
+215
-209
@@ -1,18 +1,17 @@
|
||||
//! Data objects used in the (RRDP) repository. I.e. the publish, update, and
|
||||
//! withdraw elements, as well as the notification, snapshot and delta file
|
||||
//! definitions.
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
use crate::api::publication;
|
||||
use crate::api::Base64;
|
||||
use crate::api::EncodedHash;
|
||||
use crate::util::file;
|
||||
use crate::util::Time;
|
||||
use crate::util::xml::XmlWriter;
|
||||
|
||||
use crate::util::Time;
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const VERSION: &str = "1";
|
||||
const NS: &str = "http://www.ripe.net/rpki/rrdp";
|
||||
@@ -26,7 +25,7 @@ const NS: &str = "http://www.ripe.net/rpki/rrdp";
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PublishElement {
|
||||
base64: Base64,
|
||||
uri: uri::Rsync
|
||||
uri: uri::Rsync,
|
||||
}
|
||||
|
||||
impl PublishElement {
|
||||
@@ -34,8 +33,12 @@ impl PublishElement {
|
||||
PublishElement { base64, uri }
|
||||
}
|
||||
|
||||
pub fn base64(&self) -> &Base64 { &self.base64 }
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri }
|
||||
pub fn base64(&self) -> &Base64 {
|
||||
&self.base64
|
||||
}
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
}
|
||||
|
||||
impl From<publication::Publish> for PublishElement {
|
||||
@@ -45,7 +48,6 @@ impl From<publication::Publish> for PublishElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ UpdateElement -------------------------------------------------
|
||||
|
||||
/// The updates as used in the RRDP protocol.
|
||||
@@ -56,13 +58,19 @@ impl From<publication::Publish> for PublishElement {
|
||||
pub struct UpdateElement {
|
||||
uri: uri::Rsync,
|
||||
hash: EncodedHash,
|
||||
base64: Base64
|
||||
base64: Base64,
|
||||
}
|
||||
|
||||
impl UpdateElement {
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri }
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn base64(&self) -> &Base64 { &self.base64 }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
pub fn base64(&self) -> &Base64 {
|
||||
&self.base64
|
||||
}
|
||||
}
|
||||
|
||||
impl From<publication::Update> for UpdateElement {
|
||||
@@ -74,11 +82,13 @@ impl From<publication::Update> for UpdateElement {
|
||||
|
||||
impl Into<PublishElement> for UpdateElement {
|
||||
fn into(self) -> PublishElement {
|
||||
PublishElement { uri: self.uri, base64: self.base64 }
|
||||
PublishElement {
|
||||
uri: self.uri,
|
||||
base64: self.base64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ WithdrawElement -----------------------------------------------
|
||||
|
||||
/// The withdraws as used in the RRDP protocol.
|
||||
@@ -88,12 +98,16 @@ impl Into<PublishElement> for UpdateElement {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct WithdrawElement {
|
||||
uri: uri::Rsync,
|
||||
hash: EncodedHash
|
||||
hash: EncodedHash,
|
||||
}
|
||||
|
||||
impl WithdrawElement {
|
||||
pub fn uri(&self) -> &uri::Rsync { &self.uri }
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn uri(&self) -> &uri::Rsync {
|
||||
&self.uri
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
impl From<publication::Withdraw> for WithdrawElement {
|
||||
@@ -103,25 +117,23 @@ impl From<publication::Withdraw> for WithdrawElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Notification {
|
||||
session: String,
|
||||
serial: u64,
|
||||
time: Time,
|
||||
snapshot: SnapshotRef,
|
||||
deltas: Vec<DeltaRef>,
|
||||
old_refs: Vec<(Time, FileRef)>
|
||||
session: String,
|
||||
serial: u64,
|
||||
time: Time,
|
||||
snapshot: SnapshotRef,
|
||||
deltas: Vec<DeltaRef>,
|
||||
old_refs: Vec<(Time, FileRef)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct NotificationUpdate {
|
||||
time: Time,
|
||||
session: Option<String>,
|
||||
snapshot: SnapshotRef,
|
||||
delta: DeltaRef,
|
||||
last_delta: u64
|
||||
time: Time,
|
||||
session: Option<String>,
|
||||
snapshot: SnapshotRef,
|
||||
delta: DeltaRef,
|
||||
last_delta: u64,
|
||||
}
|
||||
|
||||
impl NotificationUpdate {
|
||||
@@ -130,21 +142,33 @@ impl NotificationUpdate {
|
||||
session: Option<String>,
|
||||
snapshot: SnapshotRef,
|
||||
delta: DeltaRef,
|
||||
last_delta: u64
|
||||
last_delta: u64,
|
||||
) -> Self {
|
||||
NotificationUpdate { time, session, snapshot, delta, last_delta }
|
||||
NotificationUpdate {
|
||||
time,
|
||||
session,
|
||||
snapshot,
|
||||
delta,
|
||||
last_delta,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct NotificationCreate {
|
||||
session: String,
|
||||
snapshot: SnapshotRef
|
||||
session: String,
|
||||
snapshot: SnapshotRef,
|
||||
}
|
||||
|
||||
impl NotificationUpdate {
|
||||
pub fn unwrap(self) -> (Time, Option<String>, SnapshotRef, DeltaRef, u64) {
|
||||
(self.time, self.session, self.snapshot, self.delta, self.last_delta)
|
||||
(
|
||||
self.time,
|
||||
self.session,
|
||||
self.snapshot,
|
||||
self.delta,
|
||||
self.last_delta,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +204,7 @@ impl Notification {
|
||||
|
||||
/// Cleans up all old references from before the given time.
|
||||
pub fn clean_up(&mut self, t: Time) {
|
||||
self.old_refs.retain(|old_ref| {! old_ref.0.on_or_before(&t)})
|
||||
self.old_refs.retain(|old_ref| !old_ref.0.on_or_before(&t))
|
||||
}
|
||||
|
||||
pub fn create(session: String, snapshot: SnapshotRef) -> Self {
|
||||
@@ -190,7 +214,7 @@ impl Notification {
|
||||
time: Time::now(),
|
||||
snapshot,
|
||||
deltas: vec![],
|
||||
old_refs: vec![]
|
||||
old_refs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,8 +222,7 @@ impl Notification {
|
||||
debug!("Writing notification file: {}", path.to_string_lossy());
|
||||
let mut file = file::create_file_with_path(&path)?;
|
||||
|
||||
XmlWriter::encode_to_file(& mut file, |w| {
|
||||
|
||||
XmlWriter::encode_to_file(&mut file, |w| {
|
||||
let a = [
|
||||
("xmlns", NS),
|
||||
("version", VERSION),
|
||||
@@ -207,75 +230,64 @@ impl Notification {
|
||||
("serial", &format!("{}", self.serial)),
|
||||
];
|
||||
|
||||
w.put_element(
|
||||
"notification",
|
||||
Some(&a),
|
||||
|w| {
|
||||
{
|
||||
// snapshot ref
|
||||
let uri = self.snapshot.uri.to_string();
|
||||
let a = [
|
||||
("uri", uri.as_str()),
|
||||
("hash", self.snapshot.hash.as_ref())
|
||||
];
|
||||
w.put_element(
|
||||
"snapshot",
|
||||
Some(&a),
|
||||
|w| { w.empty() }
|
||||
)?;
|
||||
}
|
||||
|
||||
{
|
||||
// delta refs
|
||||
for delta in &self.deltas {
|
||||
let serial = format!("{}", delta.serial);
|
||||
let uri = delta.file_ref.uri.to_string();
|
||||
let a = [
|
||||
("serial", serial.as_ref()),
|
||||
("uri", uri.as_str()),
|
||||
("hash", delta.file_ref.hash.as_ref())
|
||||
];
|
||||
w.put_element(
|
||||
"delta",
|
||||
Some(&a),
|
||||
|w| { w.empty() }
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
w.put_element("notification", Some(&a), |w| {
|
||||
{
|
||||
// snapshot ref
|
||||
let uri = self.snapshot.uri.to_string();
|
||||
let a = [("uri", uri.as_str()), ("hash", self.snapshot.hash.as_ref())];
|
||||
w.put_element("snapshot", Some(&a), |w| w.empty())?;
|
||||
}
|
||||
)
|
||||
|
||||
{
|
||||
// delta refs
|
||||
for delta in &self.deltas {
|
||||
let serial = format!("{}", delta.serial);
|
||||
let uri = delta.file_ref.uri.to_string();
|
||||
let a = [
|
||||
("serial", serial.as_ref()),
|
||||
("uri", uri.as_str()),
|
||||
("hash", delta.file_ref.hash.as_ref()),
|
||||
];
|
||||
w.put_element("delta", Some(&a), |w| w.empty())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct FileRef {
|
||||
uri: uri::Https,
|
||||
path: PathBuf,
|
||||
hash: EncodedHash,
|
||||
uri: uri::Https,
|
||||
path: PathBuf,
|
||||
hash: EncodedHash,
|
||||
}
|
||||
|
||||
impl FileRef {
|
||||
pub fn new(uri: uri::Https, path: PathBuf, hash: EncodedHash) -> Self {
|
||||
FileRef { uri, path, hash }
|
||||
}
|
||||
pub fn uri(&self) -> &uri::Https { &self.uri }
|
||||
pub fn path(&self) -> &PathBuf { &self.path }
|
||||
pub fn hash(&self) -> &EncodedHash { &self.hash }
|
||||
pub fn uri(&self) -> &uri::Https {
|
||||
&self.uri
|
||||
}
|
||||
pub fn path(&self) -> &PathBuf {
|
||||
&self.path
|
||||
}
|
||||
pub fn hash(&self) -> &EncodedHash {
|
||||
&self.hash
|
||||
}
|
||||
}
|
||||
|
||||
pub type SnapshotRef = FileRef;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct DeltaRef {
|
||||
serial: u64,
|
||||
file_ref: FileRef
|
||||
serial: u64,
|
||||
file_ref: FileRef,
|
||||
}
|
||||
|
||||
impl DeltaRef {
|
||||
@@ -283,7 +295,9 @@ impl DeltaRef {
|
||||
DeltaRef { serial, file_ref }
|
||||
}
|
||||
|
||||
pub fn serial(&self) -> u64 { self.serial }
|
||||
pub fn serial(&self) -> u64 {
|
||||
self.serial
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<FileRef> for DeltaRef {
|
||||
@@ -292,7 +306,6 @@ impl AsRef<FileRef> for DeltaRef {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CurrentObjects ------------------------------------------------
|
||||
|
||||
/// Defines a current set of published elements.
|
||||
@@ -322,19 +335,22 @@ impl CurrentObjects {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ VerificationError ---------------------------------------------
|
||||
|
||||
/// Issues with relation to verifying deltas.
|
||||
#[derive(Clone, Debug, Display)]
|
||||
pub enum VerificationError {
|
||||
#[display(fmt="Publishing ({}) outside of jail URI ({}) is not allowed.", _0, _1)]
|
||||
#[display(
|
||||
fmt = "Publishing ({}) outside of jail URI ({}) is not allowed.",
|
||||
_0,
|
||||
_1
|
||||
)]
|
||||
UriOutsideJail(uri::Rsync, uri::Rsync),
|
||||
|
||||
#[display(fmt="File already exists for uri (use update!): {}", _0)]
|
||||
#[display(fmt = "File already exists for uri (use update!): {}", _0)]
|
||||
ObjectAlreadyPresent(uri::Rsync),
|
||||
|
||||
#[display(fmt="File does not match hash at uri: {}", _0)]
|
||||
#[display(fmt = "File does not match hash at uri: {}", _0)]
|
||||
NoObjectForHashAndOrUri(uri::Rsync),
|
||||
}
|
||||
|
||||
@@ -353,42 +369,36 @@ impl VerificationError {
|
||||
}
|
||||
|
||||
impl CurrentObjects {
|
||||
|
||||
fn has_match(
|
||||
&self,
|
||||
hash: &EncodedHash,
|
||||
uri: &uri::Rsync
|
||||
) -> bool {
|
||||
fn has_match(&self, hash: &EncodedHash, uri: &uri::Rsync) -> bool {
|
||||
match self.0.get(hash) {
|
||||
Some(el) => el.uri() == uri,
|
||||
None => false
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_delta(
|
||||
&self,
|
||||
delta: &DeltaElements,
|
||||
jail: &uri::Rsync
|
||||
jail: &uri::Rsync,
|
||||
) -> Result<(), VerificationError> {
|
||||
|
||||
for p in delta.publishes() {
|
||||
if ! jail.is_parent_of(p.uri()) {
|
||||
return Err(VerificationError::outside(jail, p.uri()))
|
||||
if !jail.is_parent_of(p.uri()) {
|
||||
return Err(VerificationError::outside(jail, p.uri()));
|
||||
}
|
||||
let hash = p.base64().to_encoded_hash();
|
||||
if self.0.contains_key(&hash) {
|
||||
return Err(VerificationError::present(p.uri()))
|
||||
return Err(VerificationError::present(p.uri()));
|
||||
}
|
||||
}
|
||||
|
||||
for u in delta.updates() {
|
||||
if ! self.has_match(u.hash(), u.uri()) {
|
||||
if !self.has_match(u.hash(), u.uri()) {
|
||||
return Err(VerificationError::no_match(u.uri()));
|
||||
}
|
||||
}
|
||||
|
||||
for w in delta.withdraws() {
|
||||
if ! self.has_match(w.hash(), w.uri()) {
|
||||
if !self.has_match(w.hash(), w.uri()) {
|
||||
return Err(VerificationError::no_match(w.uri()));
|
||||
}
|
||||
}
|
||||
@@ -418,22 +428,29 @@ impl CurrentObjects {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { self.0.len() }
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn to_list_reply(&self) -> publication::ListReply {
|
||||
let elements = self.0.iter().map(|el| {
|
||||
let hash = el.0.clone();
|
||||
let uri = el.1.uri().clone();
|
||||
publication::ListElement::new(uri, hash)
|
||||
}).collect();
|
||||
let elements = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|el| {
|
||||
let hash = el.0.clone();
|
||||
let uri = el.1.uri().clone();
|
||||
publication::ListElement::new(uri, hash)
|
||||
})
|
||||
.collect();
|
||||
|
||||
publication::ListReply::new(elements)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Snapshot ------------------------------------------------------
|
||||
|
||||
/// A structure to contain the RRDP snapshot data.
|
||||
@@ -441,13 +458,17 @@ impl CurrentObjects {
|
||||
pub struct Snapshot {
|
||||
session: String,
|
||||
serial: u64,
|
||||
current_objects: CurrentObjects
|
||||
current_objects: CurrentObjects,
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub fn new(session: String) -> Self {
|
||||
let current_objects = CurrentObjects::default();
|
||||
Snapshot { session, serial: 0, current_objects }
|
||||
Snapshot {
|
||||
session,
|
||||
serial: 0,
|
||||
current_objects,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_delta(&mut self, delta: Delta) {
|
||||
@@ -457,9 +478,13 @@ impl Snapshot {
|
||||
self.current_objects.apply_delta(elements)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { self.current_objects.len() }
|
||||
pub fn len(&self) -> usize {
|
||||
self.current_objects.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.current_objects.is_empty() }
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.current_objects.is_empty()
|
||||
}
|
||||
|
||||
pub fn write_xml(&self, path: &PathBuf) -> Result<EncodedHash, io::Error> {
|
||||
let vec = XmlWriter::encode_vec(|w| {
|
||||
@@ -470,24 +495,14 @@ impl Snapshot {
|
||||
("serial", &format!("{}", self.serial)),
|
||||
];
|
||||
|
||||
w.put_element(
|
||||
"snapshot",
|
||||
Some(&a),
|
||||
|w| {
|
||||
for el in self.current_objects.elements() {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [ ("uri", uri.as_ref())];
|
||||
w.put_element(
|
||||
"publish",
|
||||
Some(&atr),
|
||||
|w| {
|
||||
w.put_text(el.base64.as_ref())
|
||||
}
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
w.put_element("snapshot", Some(&a), |w| {
|
||||
for el in self.current_objects.elements() {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [("uri", uri.as_ref())];
|
||||
w.put_element("publish", Some(&atr), |w| w.put_text(el.base64.as_ref()))?;
|
||||
}
|
||||
)
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
let bytes = Bytes::from(vec);
|
||||
|
||||
@@ -498,7 +513,6 @@ impl Snapshot {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ DeltaElements -------------------------------------------------
|
||||
|
||||
/// Defines the elements for an RRDP delta.
|
||||
@@ -506,7 +520,7 @@ impl Snapshot {
|
||||
pub struct DeltaElements {
|
||||
publishes: Vec<PublishElement>,
|
||||
updates: Vec<UpdateElement>,
|
||||
withdraws: Vec<WithdrawElement>
|
||||
withdraws: Vec<WithdrawElement>,
|
||||
}
|
||||
|
||||
impl From<publication::PublishDelta> for DeltaElements {
|
||||
@@ -517,14 +531,22 @@ impl From<publication::PublishDelta> for DeltaElements {
|
||||
let updates = upds.into_iter().map(UpdateElement::from).collect();
|
||||
let withdraws = wdrs.into_iter().map(WithdrawElement::from).collect();
|
||||
|
||||
DeltaElements { publishes, updates, withdraws }
|
||||
DeltaElements {
|
||||
publishes,
|
||||
updates,
|
||||
withdraws,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeltaElements {
|
||||
pub fn unwrap(
|
||||
self
|
||||
) -> (Vec<PublishElement>, Vec<UpdateElement>, Vec<WithdrawElement>) {
|
||||
self,
|
||||
) -> (
|
||||
Vec<PublishElement>,
|
||||
Vec<UpdateElement>,
|
||||
Vec<WithdrawElement>,
|
||||
) {
|
||||
(self.publishes, self.updates, self.withdraws)
|
||||
}
|
||||
|
||||
@@ -549,47 +571,58 @@ impl DeltaElements {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Delta ---------------------------------------------------------
|
||||
|
||||
/// Defines an RRDP delta.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Delta {
|
||||
session: String,
|
||||
serial: u64,
|
||||
time: Time,
|
||||
elements: DeltaElements
|
||||
session: String,
|
||||
serial: u64,
|
||||
time: Time,
|
||||
elements: DeltaElements,
|
||||
}
|
||||
|
||||
impl Delta {
|
||||
pub fn new(
|
||||
session: String,
|
||||
serial: u64,
|
||||
elements: DeltaElements
|
||||
) -> Self {
|
||||
Delta { session, time: Time::now(), serial, elements }
|
||||
pub fn new(session: String, serial: u64, elements: DeltaElements) -> Self {
|
||||
Delta {
|
||||
session,
|
||||
time: Time::now(),
|
||||
serial,
|
||||
elements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session(&self) -> &str { &self.session }
|
||||
pub fn serial(&self) -> u64 { self.serial }
|
||||
pub fn time(&self) -> &Time { &self.time }
|
||||
pub fn elements(&self) -> &DeltaElements { &self.elements }
|
||||
pub fn session(&self) -> &str {
|
||||
&self.session
|
||||
}
|
||||
pub fn serial(&self) -> u64 {
|
||||
self.serial
|
||||
}
|
||||
pub fn time(&self) -> &Time {
|
||||
&self.time
|
||||
}
|
||||
pub fn elements(&self) -> &DeltaElements {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
/// Total number of elements
|
||||
///
|
||||
/// This is a cheap approximation of the size of the delta that can help
|
||||
/// in determining the choice of how many deltas to include in a
|
||||
/// notification file.
|
||||
pub fn len(&self) -> usize { self.elements.len() }
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.elements.is_empty() }
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elements.is_empty()
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (String, u64, DeltaElements) {
|
||||
(self.session, self.serial, self.elements)
|
||||
}
|
||||
|
||||
pub fn write_xml(&self, path: &PathBuf) -> Result<EncodedHash, io::Error> {
|
||||
|
||||
let vec = XmlWriter::encode_vec(|w| {
|
||||
let a = [
|
||||
("xmlns", NS),
|
||||
@@ -598,53 +631,27 @@ impl Delta {
|
||||
("serial", &format!("{}", self.serial)),
|
||||
];
|
||||
|
||||
w.put_element(
|
||||
"delta",
|
||||
Some(&a),
|
||||
|w| {
|
||||
for el in &self.elements.publishes {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [ ("uri", uri.as_ref())];
|
||||
w.put_element(
|
||||
"publish",
|
||||
Some(&atr),
|
||||
|w| {
|
||||
w.put_text(el.base64.as_ref())
|
||||
}
|
||||
)?;
|
||||
}
|
||||
|
||||
for el in &self.elements.updates {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [
|
||||
("uri", uri.as_ref()),
|
||||
("hash", el.hash.as_ref())
|
||||
];
|
||||
w.put_element(
|
||||
"publish",
|
||||
Some(&atr),
|
||||
|w| {
|
||||
w.put_text(el.base64.as_ref())
|
||||
}
|
||||
)?;
|
||||
}
|
||||
|
||||
for el in &self.elements.withdraws {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [
|
||||
("uri", uri.as_ref()),
|
||||
("hash", el.hash.as_ref())
|
||||
];
|
||||
w.put_element(
|
||||
"withdraw",
|
||||
Some(&atr),
|
||||
|w| { w.empty() }
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
w.put_element("delta", Some(&a), |w| {
|
||||
for el in &self.elements.publishes {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [("uri", uri.as_ref())];
|
||||
w.put_element("publish", Some(&atr), |w| w.put_text(el.base64.as_ref()))?;
|
||||
}
|
||||
)
|
||||
|
||||
for el in &self.elements.updates {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [("uri", uri.as_ref()), ("hash", el.hash.as_ref())];
|
||||
w.put_element("publish", Some(&atr), |w| w.put_text(el.base64.as_ref()))?;
|
||||
}
|
||||
|
||||
for el in &self.elements.withdraws {
|
||||
let uri = el.uri.to_string();
|
||||
let atr = [("uri", uri.as_ref()), ("hash", el.hash.as_ref())];
|
||||
w.put_element("withdraw", Some(&atr), |w| w.empty())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
let bytes = Bytes::from(vec);
|
||||
@@ -653,5 +660,4 @@ impl Delta {
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
use super::{
|
||||
Command,
|
||||
Event,
|
||||
Storable
|
||||
};
|
||||
|
||||
use super::{Command, Event, Storable};
|
||||
|
||||
//------------ Aggregate -----------------------------------------------------
|
||||
|
||||
pub trait Aggregate: Storable + Send + Sync + 'static {
|
||||
|
||||
type Command: Command<Event = Self::Event>;
|
||||
type Event: Event;
|
||||
type InitEvent: Event;
|
||||
@@ -48,4 +42,4 @@ pub trait Aggregate: Storable + Send + Sync + 'static {
|
||||
/// The command is moved, because we want to enable moving its data
|
||||
/// without reallocating.
|
||||
fn process_command(&self, command: Self::Command) -> Result<Vec<Self::Event>, Self::Error>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,7 @@ use std::sync::RwLock;
|
||||
|
||||
use crate::api::admin::Handle;
|
||||
|
||||
use super::{
|
||||
Aggregate,
|
||||
DiskKeyStore,
|
||||
Event,
|
||||
EventListener,
|
||||
KeyStore,
|
||||
KeyStoreError,
|
||||
};
|
||||
use super::{Aggregate, DiskKeyStore, Event, EventListener, KeyStore, KeyStoreError};
|
||||
|
||||
const SNAPSHOT_FREQ: u64 = 5;
|
||||
|
||||
@@ -47,7 +40,6 @@ pub trait AggregateStore<A: Aggregate>: Send + Sync {
|
||||
fn add_listener<L: EventListener<A>>(&mut self, listener: Arc<L>);
|
||||
}
|
||||
|
||||
|
||||
/// This type defines possible Errors for the AggregateStore
|
||||
#[derive(Debug, Display)]
|
||||
pub enum AggregateStoreError {
|
||||
@@ -68,16 +60,17 @@ pub enum AggregateStoreError {
|
||||
}
|
||||
|
||||
impl From<KeyStoreError> for AggregateStoreError {
|
||||
fn from(e: KeyStoreError) -> Self { AggregateStoreError::KeyStoreError(e) }
|
||||
fn from(e: KeyStoreError) -> Self {
|
||||
AggregateStoreError::KeyStoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct DiskAggregateStore<A: Aggregate> {
|
||||
store: DiskKeyStore,
|
||||
cache: RwLock<HashMap<Handle, Arc<A>>>,
|
||||
use_cache: bool,
|
||||
listeners: Vec<Arc<EventListener<A>>>,
|
||||
outer_lock: RwLock<()>
|
||||
outer_lock: RwLock<()>,
|
||||
}
|
||||
|
||||
impl<A: Aggregate> DiskAggregateStore<A> {
|
||||
@@ -87,17 +80,22 @@ impl<A: Aggregate> DiskAggregateStore<A> {
|
||||
let use_cache = true;
|
||||
let listeners = vec![];
|
||||
let lock = RwLock::new(());
|
||||
Ok(DiskAggregateStore { store, cache, use_cache, listeners, outer_lock: lock })
|
||||
Ok(DiskAggregateStore {
|
||||
store,
|
||||
cache,
|
||||
use_cache,
|
||||
listeners,
|
||||
outer_lock: lock,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Aggregate> DiskAggregateStore<A> {
|
||||
fn has_updates(
|
||||
&self,
|
||||
id: &Handle,
|
||||
aggregate: &A
|
||||
) -> StoreResult<bool> {
|
||||
Ok(self.store.get_event::<A::Event>(id, aggregate.version())?.is_some())
|
||||
fn has_updates(&self, id: &Handle, aggregate: &A) -> StoreResult<bool> {
|
||||
Ok(self
|
||||
.store
|
||||
.get_event::<A::Event>(id, aggregate.version())?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
fn cache_get(&self, id: &Handle) -> Option<Arc<A>> {
|
||||
@@ -117,18 +115,16 @@ impl<A: Aggregate> DiskAggregateStore<A> {
|
||||
fn get_latest_no_lock(&self, handle: &Handle) -> StoreResult<Arc<A>> {
|
||||
debug!("Trying to load aggregate id: {}", handle);
|
||||
match self.cache_get(handle) {
|
||||
None => {
|
||||
match self.store.get_aggregate(handle)? {
|
||||
None => {
|
||||
error!("Could not load aggregate with id: {} from disk", handle);
|
||||
Err(AggregateStoreError::UnknownAggregate(handle.clone()))
|
||||
},
|
||||
Some(agg) => {
|
||||
let arc: Arc<A> = Arc::new(agg);
|
||||
self.cache_update(handle, arc.clone());
|
||||
debug!("Loaded aggregate id: {} from disk", handle);
|
||||
Ok(arc)
|
||||
}
|
||||
None => match self.store.get_aggregate(handle)? {
|
||||
None => {
|
||||
error!("Could not load aggregate with id: {} from disk", handle);
|
||||
Err(AggregateStoreError::UnknownAggregate(handle.clone()))
|
||||
}
|
||||
Some(agg) => {
|
||||
let arc: Arc<A> = Arc::new(agg);
|
||||
self.cache_update(handle, arc.clone());
|
||||
debug!("Loaded aggregate id: {} from disk", handle);
|
||||
Ok(arc)
|
||||
}
|
||||
},
|
||||
Some(mut arc) => {
|
||||
@@ -149,10 +145,7 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
self.get_latest_no_lock(handle)
|
||||
}
|
||||
|
||||
fn add(
|
||||
&self,
|
||||
init: A::InitEvent
|
||||
) -> StoreResult<Arc<A>> {
|
||||
fn add(&self, init: A::InitEvent) -> StoreResult<Arc<A>> {
|
||||
let _lock = self.outer_lock.write().unwrap();
|
||||
|
||||
self.store.store_event(&init)?;
|
||||
@@ -168,13 +161,7 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
Ok(arc)
|
||||
}
|
||||
|
||||
|
||||
fn update(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
prev: Arc<A>,
|
||||
events: Vec<A::Event>
|
||||
) -> StoreResult<Arc<A>> {
|
||||
fn update(&self, handle: &Handle, prev: Arc<A>, events: Vec<A::Event>) -> StoreResult<Arc<A>> {
|
||||
let _lock = self.outer_lock.write().unwrap();
|
||||
|
||||
// Get the latest arc.
|
||||
@@ -182,7 +169,7 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
{
|
||||
// Verify whether there is a concurrency issue
|
||||
if prev.version() != latest.version() {
|
||||
return Err(AggregateStoreError::ConcurrentModification(handle.clone()))
|
||||
return Err(AggregateStoreError::ConcurrentModification(handle.clone()));
|
||||
}
|
||||
|
||||
// forget the previous version
|
||||
@@ -191,7 +178,6 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
// make the arc mutable, hopefully forgetting prev will avoid the clone
|
||||
let agg = Arc::make_mut(&mut latest);
|
||||
|
||||
|
||||
// Using a lock on the hashmap here to ensure that all updates happen sequentially.
|
||||
// It would be better to get a lock only for this specific aggregate. So it may be
|
||||
// worth rethinking the structure.
|
||||
@@ -213,8 +199,7 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
|
||||
for i in 0..nr_events {
|
||||
let event = &events[i as usize];
|
||||
if event.version() != version_before + i ||
|
||||
event.handle() != handle {
|
||||
if event.version() != version_before + i || event.handle() != handle {
|
||||
return Err(AggregateStoreError::WrongEventForAggregate);
|
||||
}
|
||||
}
|
||||
@@ -253,4 +238,4 @@ impl<A: Aggregate> AggregateStore<A> for DiskAggregateStore<A> {
|
||||
|
||||
self.listeners.push(listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::api::admin::Handle;
|
||||
|
||||
use super::Event;
|
||||
|
||||
|
||||
//------------ Command -------------------------------------------------------
|
||||
|
||||
/// Commands are used to send an intent to change an aggregate.
|
||||
@@ -33,10 +32,11 @@ pub trait Command {
|
||||
/// Note that this defaults to true, which is the safe choice when in
|
||||
/// doubt. If you choose to implement this, then you will also need to
|
||||
/// implement the ['set_affected_version'] function.
|
||||
fn conflicts(&self, _events: &[Self::Event]) -> bool { true }
|
||||
fn conflicts(&self, _events: &[Self::Event]) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ SentCommand ---------------------------------------------------
|
||||
|
||||
/// Convenience wrapper so that implementations can just implement
|
||||
@@ -45,7 +45,7 @@ pub trait Command {
|
||||
pub struct SentCommand<C: CommandDetails> {
|
||||
handle: Handle,
|
||||
version: Option<u64>,
|
||||
details: C
|
||||
details: C,
|
||||
}
|
||||
|
||||
impl<C: CommandDetails> Command for SentCommand<C> {
|
||||
@@ -61,15 +61,19 @@ impl<C: CommandDetails> Command for SentCommand<C> {
|
||||
}
|
||||
|
||||
impl<C: CommandDetails> SentCommand<C> {
|
||||
|
||||
pub fn new(id: &Handle, version: Option<u64>, details: C) -> Self {
|
||||
SentCommand { handle: id.clone(), version, details }
|
||||
SentCommand {
|
||||
handle: id.clone(),
|
||||
version,
|
||||
details,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_details(self) -> C { self.details }
|
||||
pub fn into_details(self) -> C {
|
||||
self.details
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CommandDetails ------------------------------------------------
|
||||
|
||||
/// Implement this for an enum with CommandDetails, so you you can reuse the
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::api::admin::Handle;
|
||||
|
||||
use super::Storable;
|
||||
|
||||
|
||||
//------------ Event --------------------------------------------------------
|
||||
|
||||
pub trait Event: Storable + 'static {
|
||||
@@ -20,18 +19,25 @@ pub struct StoredEvent<E: Storable + 'static> {
|
||||
id: Handle,
|
||||
version: u64,
|
||||
#[serde(deserialize_with = "E::deserialize")]
|
||||
details: E
|
||||
details: E,
|
||||
}
|
||||
|
||||
impl<E: Storable + 'static> StoredEvent<E> {
|
||||
|
||||
pub fn new(id: &Handle, version: u64, event: E) -> Self {
|
||||
StoredEvent { id: id.clone(), version, details: event }
|
||||
StoredEvent {
|
||||
id: id.clone(),
|
||||
version,
|
||||
details: event,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn details(&self) -> &E { & self.details }
|
||||
pub fn details(&self) -> &E {
|
||||
&self.details
|
||||
}
|
||||
|
||||
pub fn into_details(self) -> E { self.details }
|
||||
pub fn into_details(self) -> E {
|
||||
self.details
|
||||
}
|
||||
|
||||
/// Return the parts of this event.
|
||||
pub fn unwrap(self) -> (Handle, u64, E) {
|
||||
@@ -47,4 +53,4 @@ impl<E: Storable + 'static> Event for StoredEvent<E> {
|
||||
fn version(&self) -> u64 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,22 @@ pub trait EventListener<A: Aggregate>: Send + Sync + 'static {
|
||||
fn listen(&self, agg: &A, event: &A::Event);
|
||||
}
|
||||
|
||||
|
||||
//------------ EventCounter --------------------------------------------------
|
||||
|
||||
/// Example listener that simply counts all events
|
||||
pub struct EventCounter {
|
||||
counter: RwLock<Counter>
|
||||
counter: RwLock<Counter>,
|
||||
}
|
||||
|
||||
struct Counter {
|
||||
total: usize
|
||||
total: usize,
|
||||
}
|
||||
|
||||
impl Default for EventCounter {
|
||||
fn default() -> Self {
|
||||
EventCounter { counter: RwLock::new(Counter { total: 0 }) }
|
||||
EventCounter {
|
||||
counter: RwLock::new(Counter { total: 0 }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +1,22 @@
|
||||
//! Event sourcing support for Krill
|
||||
|
||||
mod agg;
|
||||
pub use self::agg::{
|
||||
Aggregate,
|
||||
};
|
||||
pub use self::agg::Aggregate;
|
||||
|
||||
mod evt;
|
||||
pub use self::evt::{
|
||||
Event,
|
||||
StoredEvent
|
||||
};
|
||||
pub use self::evt::{Event, StoredEvent};
|
||||
|
||||
mod cmd;
|
||||
pub use self::cmd::{
|
||||
Command,
|
||||
CommandDetails,
|
||||
SentCommand
|
||||
};
|
||||
pub use self::cmd::{Command, CommandDetails, SentCommand};
|
||||
|
||||
mod store;
|
||||
pub use self::store::{
|
||||
DiskKeyStore,
|
||||
KeyStore,
|
||||
KeyStoreError,
|
||||
Storable
|
||||
};
|
||||
pub use self::store::{DiskKeyStore, KeyStore, KeyStoreError, Storable};
|
||||
|
||||
mod agg_store;
|
||||
pub use self::agg_store::{
|
||||
AggregateStore,
|
||||
AggregateStoreError,
|
||||
DiskAggregateStore
|
||||
};
|
||||
pub use self::agg_store::{AggregateStore, AggregateStoreError, DiskAggregateStore};
|
||||
|
||||
mod listener;
|
||||
pub use self::listener::{
|
||||
EventCounter,
|
||||
EventListener
|
||||
};
|
||||
|
||||
pub use self::listener::{EventCounter, EventListener};
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
@@ -69,19 +47,23 @@ mod tests {
|
||||
type InitPersonEvent = StoredEvent<InitPersonDetails>;
|
||||
|
||||
impl InitPersonEvent {
|
||||
|
||||
pub fn init(id: &Handle, name: &str) -> Self {
|
||||
StoredEvent::new(id, 0, InitPersonDetails { name: name.to_string()})
|
||||
StoredEvent::new(
|
||||
id,
|
||||
0,
|
||||
InitPersonDetails {
|
||||
name: name.to_string(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
struct InitPersonDetails {
|
||||
pub name: String
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
//------------ InitPersonEvent -----------------------------------------------
|
||||
//------------ InitPersonEvent -----------------------------------------------
|
||||
|
||||
/// Every aggregate defines their own set of events - i.e. state changes. The
|
||||
/// state of an aggregate can only change when events are applied. And events
|
||||
@@ -97,7 +79,7 @@ mod tests {
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
enum PersonEventDetails {
|
||||
NameChanged(String),
|
||||
HadBirthday
|
||||
HadBirthday,
|
||||
}
|
||||
|
||||
impl PersonEvent {
|
||||
@@ -106,14 +88,10 @@ mod tests {
|
||||
}
|
||||
|
||||
pub fn name_changed(p: &Person, name: String) -> Self {
|
||||
StoredEvent::new(
|
||||
p.id(),
|
||||
p.version,
|
||||
PersonEventDetails::NameChanged(name))
|
||||
StoredEvent::new(p.id(), p.version, PersonEventDetails::NameChanged(name))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PersonCommand -------------------------------------------------
|
||||
|
||||
/// In order to change an aggregate a command is sent to it. The aggregate
|
||||
@@ -134,7 +112,7 @@ mod tests {
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
enum PersonCommandDetails {
|
||||
ChangeName(String),
|
||||
GoAroundTheSun
|
||||
GoAroundTheSun,
|
||||
}
|
||||
|
||||
impl CommandDetails for PersonCommandDetails {
|
||||
@@ -142,12 +120,10 @@ mod tests {
|
||||
}
|
||||
|
||||
impl PersonCommand {
|
||||
|
||||
pub fn go_around_sun(id: &Handle, version: Option<u64>) -> Self {
|
||||
Self::new(id, version, PersonCommandDetails::GoAroundTheSun)
|
||||
}
|
||||
|
||||
|
||||
pub fn change_name(id: &Handle, version: Option<u64>, s: &str) -> Self {
|
||||
let details = PersonCommandDetails::ChangeName(s.to_string());
|
||||
Self::new(id, version, details)
|
||||
@@ -161,19 +137,17 @@ mod tests {
|
||||
#[derive(Clone, Debug, Display)]
|
||||
enum PersonError {
|
||||
#[display(fmt = "No person can live longer than 255 years")]
|
||||
TooOld
|
||||
TooOld,
|
||||
}
|
||||
|
||||
impl std::error::Error for PersonError {}
|
||||
|
||||
|
||||
//------------ PersonResult --------------------------------------------------
|
||||
|
||||
/// A shorthand for the result type returned by the process_command function
|
||||
/// of the Person aggregate.
|
||||
type PersonResult = Result<Vec<PersonEvent>, PersonError>;
|
||||
|
||||
|
||||
//------------ Person ------------------------------------------------------
|
||||
|
||||
/// Defines a person object. Persons have a name and an age.
|
||||
@@ -191,14 +165,22 @@ mod tests {
|
||||
version: u64,
|
||||
|
||||
name: String,
|
||||
age: u8
|
||||
age: u8,
|
||||
}
|
||||
|
||||
impl Person {
|
||||
pub fn id(&self) -> &Handle { &self.id }
|
||||
pub fn version(&self) -> u64 { self.version }
|
||||
pub fn name(&self) -> &String { &self.name }
|
||||
pub fn age(&self) -> u8 { self.age }
|
||||
pub fn id(&self) -> &Handle {
|
||||
&self.id
|
||||
}
|
||||
pub fn version(&self) -> u64 {
|
||||
self.version
|
||||
}
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
pub fn age(&self) -> u8 {
|
||||
self.age
|
||||
}
|
||||
}
|
||||
|
||||
impl Aggregate for Person {
|
||||
@@ -210,7 +192,10 @@ mod tests {
|
||||
fn init(event: InitPersonEvent) -> Result<Self, PersonError> {
|
||||
let (id, _version, init) = event.unwrap();
|
||||
Ok(Person {
|
||||
id, version: 1, name: init.name, age: 0
|
||||
id,
|
||||
version: 1,
|
||||
name: init.name,
|
||||
age: 0,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -220,8 +205,8 @@ mod tests {
|
||||
|
||||
fn apply(&mut self, event: PersonEvent) {
|
||||
match event.into_details() {
|
||||
PersonEventDetails::NameChanged(name) => { self.name = name },
|
||||
PersonEventDetails::HadBirthday => { self.age += 1 }
|
||||
PersonEventDetails::NameChanged(name) => self.name = name,
|
||||
PersonEventDetails::HadBirthday => self.age += 1,
|
||||
}
|
||||
self.version += 1;
|
||||
}
|
||||
@@ -231,7 +216,7 @@ mod tests {
|
||||
PersonCommandDetails::ChangeName(name) => {
|
||||
let event = PersonEvent::name_changed(&self, name);
|
||||
Ok(vec![event])
|
||||
},
|
||||
}
|
||||
PersonCommandDetails::GoAroundTheSun => {
|
||||
if self.age == 255 {
|
||||
Err(PersonError::TooOld)
|
||||
@@ -247,7 +232,6 @@ mod tests {
|
||||
#[test]
|
||||
fn test() {
|
||||
test::test_under_tmp(|d| {
|
||||
|
||||
let counter = Arc::new(EventCounter::default());
|
||||
let mut manager = DiskAggregateStore::<Person>::new(&d, "person").unwrap();
|
||||
manager.add_listener(counter.clone());
|
||||
@@ -269,7 +253,7 @@ mod tests {
|
||||
|
||||
age += 1;
|
||||
if age == 21 {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +274,6 @@ mod tests {
|
||||
assert_eq!(21, alice.age());
|
||||
|
||||
assert_eq!(22, counter.total())
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,30 +5,24 @@ use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
|
||||
use crate::api::admin::Handle;
|
||||
use crate::util::file;
|
||||
|
||||
use super::{
|
||||
Aggregate,
|
||||
Event,
|
||||
};
|
||||
|
||||
use super::{Aggregate, Event};
|
||||
|
||||
//------------ Storable ------------------------------------------------------
|
||||
|
||||
pub trait Storable: Clone + Serialize + DeserializeOwned + Sized + 'static {}
|
||||
impl<T: Clone + Serialize + DeserializeOwned + Sized + 'static> Storable for T { }
|
||||
|
||||
impl<T: Clone + Serialize + DeserializeOwned + Sized + 'static> Storable for T {}
|
||||
|
||||
//------------ KeyStore ------------------------------------------------------
|
||||
|
||||
/// Generic KeyStore for AggregateManager
|
||||
pub trait KeyStore {
|
||||
|
||||
type Key;
|
||||
|
||||
fn key_for_snapshot() -> Self::Key;
|
||||
@@ -38,7 +32,6 @@ pub trait KeyStore {
|
||||
|
||||
fn has_key(&self, id: &Handle, key: &Self::Key) -> bool;
|
||||
|
||||
|
||||
fn has_aggregate(&self, id: &Handle) -> bool;
|
||||
|
||||
fn aggregates(&self) -> Vec<Handle>; // Use Iterator?
|
||||
@@ -49,7 +42,7 @@ pub trait KeyStore {
|
||||
&self,
|
||||
id: &Handle,
|
||||
key: &Self::Key,
|
||||
value: &V
|
||||
value: &V,
|
||||
) -> Result<(), KeyStoreError>;
|
||||
|
||||
/// Get the value for this key, if any exists.
|
||||
@@ -57,39 +50,28 @@ pub trait KeyStore {
|
||||
fn get<V: Any + Storable>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
key: &Self::Key
|
||||
key: &Self::Key,
|
||||
) -> Result<Option<V>, KeyStoreError>;
|
||||
|
||||
/// Get the value for this key, if any exists.
|
||||
|
||||
fn get_event<V: Event>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
version: u64
|
||||
) -> Result<Option<V>, KeyStoreError>;
|
||||
fn get_event<V: Event>(&self, id: &Handle, version: u64) -> Result<Option<V>, KeyStoreError>;
|
||||
|
||||
fn store_event<V: Event>(
|
||||
&self,
|
||||
event: &V
|
||||
) -> Result<(), KeyStoreError>;
|
||||
fn store_event<V: Event>(&self, event: &V) -> Result<(), KeyStoreError>;
|
||||
|
||||
/// Get the latest aggregate
|
||||
|
||||
fn get_aggregate<V: Aggregate>(
|
||||
&self,
|
||||
id: &Handle
|
||||
) -> Result<Option<V>, KeyStoreError>;
|
||||
fn get_aggregate<V: Aggregate>(&self, id: &Handle) -> Result<Option<V>, KeyStoreError>;
|
||||
|
||||
/// Saves the latest snapshot - overwrites any previous snapshot.
|
||||
|
||||
fn store_aggregate<V: Aggregate>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
aggregate: &V
|
||||
aggregate: &V,
|
||||
) -> Result<(), KeyStoreError>;
|
||||
}
|
||||
|
||||
|
||||
//------------ KeyStoreError -------------------------------------------------
|
||||
|
||||
/// This type defines possible Errors for KeyStore
|
||||
@@ -105,19 +87,22 @@ pub enum KeyStoreError {
|
||||
KeyExists(String),
|
||||
|
||||
#[display(fmt = "Aggregate init event exists, but cannot be applied")]
|
||||
InitError
|
||||
InitError,
|
||||
}
|
||||
|
||||
impl From<io::Error> for KeyStoreError {
|
||||
fn from(e: io::Error) -> Self { KeyStoreError::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
KeyStoreError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for KeyStoreError {
|
||||
fn from(e: serde_json::Error) -> Self { KeyStoreError::JsonError(e) }
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
KeyStoreError::JsonError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for KeyStoreError { }
|
||||
|
||||
impl std::error::Error for KeyStoreError {}
|
||||
|
||||
//------------ DiskKeyStore --------------------------------------------------
|
||||
|
||||
@@ -166,7 +151,7 @@ impl KeyStore for DiskKeyStore {
|
||||
&self,
|
||||
id: &Handle,
|
||||
key: &Self::Key,
|
||||
value: &V
|
||||
value: &V,
|
||||
) -> Result<(), KeyStoreError> {
|
||||
let mut f = file::create_file_with_path(&self.file_path(id, key))?;
|
||||
let json = serde_json::to_string_pretty(value)?;
|
||||
@@ -177,7 +162,7 @@ impl KeyStore for DiskKeyStore {
|
||||
fn get<V: Any + Storable>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
key: &Self::Key
|
||||
key: &Self::Key,
|
||||
) -> Result<Option<V>, KeyStoreError> {
|
||||
let path = self.file_path(id, key);
|
||||
let path_str = path.to_string_lossy().into_owned();
|
||||
@@ -188,7 +173,7 @@ impl KeyStore for DiskKeyStore {
|
||||
Err(e) => {
|
||||
error!("Could not deserialize json at: {}, error: {}", path_str, e);
|
||||
Err(KeyStoreError::JsonError(e))
|
||||
},
|
||||
}
|
||||
Ok(v) => {
|
||||
debug!("Deserialized json at: {}", path_str);
|
||||
Ok(Some(v))
|
||||
@@ -201,11 +186,7 @@ impl KeyStore for DiskKeyStore {
|
||||
}
|
||||
|
||||
/// Get the value for this key, if any exists.
|
||||
fn get_event<V: Event>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
version: u64
|
||||
) -> Result<Option<V>, KeyStoreError> {
|
||||
fn get_event<V: Event>(&self, id: &Handle, version: u64) -> Result<Option<V>, KeyStoreError> {
|
||||
let path = self.path_for_event(id, version);
|
||||
let path_str = path.to_string_lossy().into_owned();
|
||||
|
||||
@@ -215,7 +196,7 @@ impl KeyStore for DiskKeyStore {
|
||||
Err(e) => {
|
||||
error!("Could not deserialize json at: {}, error: {}", path_str, e);
|
||||
Err(KeyStoreError::JsonError(e))
|
||||
},
|
||||
}
|
||||
Ok(v) => {
|
||||
debug!("Deserialized event at: {}", path_str);
|
||||
Ok(Some(v))
|
||||
@@ -227,10 +208,7 @@ impl KeyStore for DiskKeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn store_event<V: Event>(
|
||||
&self,
|
||||
event: &V
|
||||
) -> Result<(), KeyStoreError> {
|
||||
fn store_event<V: Event>(&self, event: &V) -> Result<(), KeyStoreError> {
|
||||
let id = event.handle();
|
||||
let key = Self::key_for_event(event.version());
|
||||
if self.has_key(id, &key) {
|
||||
@@ -240,22 +218,17 @@ impl KeyStore for DiskKeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_aggregate<V: Aggregate>(
|
||||
&self,
|
||||
id: &Handle
|
||||
) -> Result<Option<V>, KeyStoreError> {
|
||||
fn get_aggregate<V: Aggregate>(&self, id: &Handle) -> Result<Option<V>, KeyStoreError> {
|
||||
// try to get a snapshot.
|
||||
// If that fails, try to get the init event.
|
||||
// Then replay all newer events that can be found.
|
||||
let key = Self::key_for_snapshot();
|
||||
let aggregate_opt = match self.get::<V>(id, &key)? {
|
||||
Some(aggregate) => Some(aggregate),
|
||||
None => {
|
||||
match self.get_event::<V::InitEvent>(id, 0)? {
|
||||
Some(e) => Some(V::init(e).map_err(|_|KeyStoreError::InitError)?),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
None => match self.get_event::<V::InitEvent>(id, 0)? {
|
||||
Some(e) => Some(V::init(e).map_err(|_| KeyStoreError::InitError)?),
|
||||
None => None,
|
||||
},
|
||||
};
|
||||
|
||||
match aggregate_opt {
|
||||
@@ -270,7 +243,7 @@ impl KeyStore for DiskKeyStore {
|
||||
fn store_aggregate<V: Aggregate>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
aggregate: &V
|
||||
aggregate: &V,
|
||||
) -> Result<(), KeyStoreError> {
|
||||
let key = Self::key_for_snapshot();
|
||||
self.store(id, &key, aggregate)
|
||||
@@ -285,13 +258,10 @@ impl DiskKeyStore {
|
||||
}
|
||||
|
||||
/// Creates a directory for the name_space under the work_dir.
|
||||
pub fn under_work_dir(
|
||||
work_dir: &PathBuf,
|
||||
name_space: &str
|
||||
) -> Result<Self, io::Error> {
|
||||
pub fn under_work_dir(work_dir: &PathBuf, name_space: &str) -> Result<Self, io::Error> {
|
||||
let mut path = work_dir.clone();
|
||||
path.push(name_space);
|
||||
if ! path.is_dir() {
|
||||
if !path.is_dir() {
|
||||
fs::create_dir_all(&path)?;
|
||||
}
|
||||
Ok(Self::new(work_dir, name_space))
|
||||
@@ -309,11 +279,7 @@ impl DiskKeyStore {
|
||||
dir_path
|
||||
}
|
||||
|
||||
fn path_for_event(
|
||||
&self,
|
||||
id: &Handle,
|
||||
version: u64
|
||||
) -> PathBuf {
|
||||
fn path_for_event(&self, id: &Handle, version: u64) -> PathBuf {
|
||||
let mut file_path = self.dir_for_aggregate(id);
|
||||
file_path.push(format!("delta-{}.json", version));
|
||||
file_path
|
||||
@@ -322,11 +288,11 @@ impl DiskKeyStore {
|
||||
pub fn update_aggregate<A: Aggregate>(
|
||||
&self,
|
||||
id: &Handle,
|
||||
aggregate: &mut A
|
||||
aggregate: &mut A,
|
||||
) -> Result<(), KeyStoreError> {
|
||||
while let Some(e) = self.get_event(id, aggregate.version())? {
|
||||
aggregate.apply(e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -1,22 +1,26 @@
|
||||
//! Common types used by the various Krill components.
|
||||
|
||||
extern crate base64;
|
||||
#[macro_use] extern crate bcder;
|
||||
#[macro_use]
|
||||
extern crate bcder;
|
||||
extern crate bytes;
|
||||
extern crate chrono;
|
||||
#[macro_use] extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
extern crate futures;
|
||||
extern crate hex;
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate rand;
|
||||
extern crate reqwest;
|
||||
extern crate rpki;
|
||||
#[macro_use] extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
extern crate core;
|
||||
extern crate serde_json;
|
||||
extern crate syslog;
|
||||
extern crate xml as xmlrs;
|
||||
extern crate core;
|
||||
|
||||
pub mod api;
|
||||
pub mod eventsourcing;
|
||||
|
||||
+47
-21
@@ -15,33 +15,38 @@ pub struct ClientAuth {
|
||||
}
|
||||
|
||||
impl ClientAuth {
|
||||
pub fn new(
|
||||
cert: IdCert,
|
||||
) -> Self {
|
||||
pub fn new(cert: IdCert) -> Self {
|
||||
ClientAuth { cert }
|
||||
}
|
||||
pub fn cert(&self) -> &IdCert { &self.cert }
|
||||
pub fn set_cert(&mut self, cert: IdCert) { self.cert = cert; }
|
||||
pub fn cert(&self) -> &IdCert {
|
||||
&self.cert
|
||||
}
|
||||
pub fn set_cert(&mut self, cert: IdCert) {
|
||||
self.cert = cert;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ClientInfo ---------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct ClientInfo {
|
||||
handle: Handle,
|
||||
auth: ClientAuth
|
||||
auth: ClientAuth,
|
||||
}
|
||||
|
||||
impl ClientInfo {
|
||||
pub fn new(handle: Handle, auth: ClientAuth) -> Self {
|
||||
ClientInfo { handle, auth }
|
||||
}
|
||||
pub fn unwrap(self) -> (Handle, ClientAuth ) {
|
||||
pub fn unwrap(self) -> (Handle, ClientAuth) {
|
||||
(self.handle, self.auth)
|
||||
}
|
||||
pub fn handle(&self) -> &Handle { &self.handle }
|
||||
pub fn auth(&self) -> &ClientAuth { &self.auth }
|
||||
pub fn handle(&self) -> &Handle {
|
||||
&self.handle
|
||||
}
|
||||
pub fn auth(&self) -> &ClientAuth {
|
||||
&self.auth
|
||||
}
|
||||
}
|
||||
|
||||
//------------ CmsClientInfo -----------------------------------------------
|
||||
@@ -59,17 +64,38 @@ impl CmsClientInfo {
|
||||
handle: Handle,
|
||||
cert: IdCert,
|
||||
key_id: SignerKeyId,
|
||||
publication_uri: uri::Https
|
||||
publication_uri: uri::Https,
|
||||
) -> Self {
|
||||
CmsClientInfo { handle, server_cert: cert, key_id, publication_uri }
|
||||
CmsClientInfo {
|
||||
handle,
|
||||
server_cert: cert,
|
||||
key_id,
|
||||
publication_uri,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> &Handle { &self.handle }
|
||||
pub fn set_handle(&mut self, handle: Handle) { self.handle = handle; }
|
||||
pub fn server_cert(&self) -> &IdCert { &self.server_cert }
|
||||
pub fn set_server_cert(&mut self, cert: IdCert) { self.server_cert = cert; }
|
||||
pub fn key_id(&self) -> &SignerKeyId { &self.key_id }
|
||||
pub fn set_key_id(&mut self, key_id: SignerKeyId) { self.key_id = key_id; }
|
||||
pub fn publication_uri(&self) -> &uri::Https { &self.publication_uri }
|
||||
pub fn set_publication_uri(&mut self, uri: uri::Https) { self.publication_uri = uri; }
|
||||
}
|
||||
pub fn handle(&self) -> &Handle {
|
||||
&self.handle
|
||||
}
|
||||
pub fn set_handle(&mut self, handle: Handle) {
|
||||
self.handle = handle;
|
||||
}
|
||||
pub fn server_cert(&self) -> &IdCert {
|
||||
&self.server_cert
|
||||
}
|
||||
pub fn set_server_cert(&mut self, cert: IdCert) {
|
||||
self.server_cert = cert;
|
||||
}
|
||||
pub fn key_id(&self) -> &SignerKeyId {
|
||||
&self.key_id
|
||||
}
|
||||
pub fn set_key_id(&mut self, key_id: SignerKeyId) {
|
||||
self.key_id = key_id;
|
||||
}
|
||||
pub fn publication_uri(&self) -> &uri::Https {
|
||||
&self.publication_uri
|
||||
}
|
||||
pub fn set_publication_uri(&mut self, uri: uri::Https) {
|
||||
self.publication_uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
+147
-273
@@ -1,44 +1,32 @@
|
||||
//! Support for building RPKI Certificates and Objects
|
||||
use std::fmt;
|
||||
use bcder::{BitString, Mode, OctetString, Oid, Tag};
|
||||
use bcder::{decode, encode};
|
||||
use bcder::encode::{Constructed, PrimitiveContent, Values};
|
||||
use bcder::{decode, encode};
|
||||
use bcder::{BitString, Mode, OctetString, Oid, Tag};
|
||||
use bytes::Bytes;
|
||||
use chrono::Utc;
|
||||
use rpki::cert::ext::{
|
||||
AuthorityKeyIdentifier,
|
||||
CrlNumber,
|
||||
Extensions,
|
||||
KeyIdentifier
|
||||
};
|
||||
use rpki::cert::ext::{AuthorityKeyIdentifier, CrlNumber, Extensions, KeyIdentifier};
|
||||
use rpki::crl::Crl;
|
||||
use rpki::crypto::{
|
||||
DigestAlgorithm,
|
||||
Signature,
|
||||
SignatureAlgorithm,
|
||||
Signer,
|
||||
SigningError,
|
||||
PublicKey
|
||||
};
|
||||
use rpki::crypto::signer::KeyError;
|
||||
use rpki::crypto::{
|
||||
DigestAlgorithm, PublicKey, Signature, SignatureAlgorithm, Signer, SigningError,
|
||||
};
|
||||
use rpki::oid;
|
||||
use rpki::x509::{Name, Validity, Time};
|
||||
use rpki::x509::{Name, Time, Validity};
|
||||
use std::fmt;
|
||||
|
||||
use crate::remote::id::{IdCert, IdExtensions};
|
||||
|
||||
|
||||
//------------ TbsCertificate ------------------------------------------------
|
||||
|
||||
/// The supported extension types for our RPKI TbsCertificate
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum RpkiTbsExtension {
|
||||
ResourceExtensions(Extensions),
|
||||
IdExtensions(IdExtensions)
|
||||
IdExtensions(IdExtensions),
|
||||
}
|
||||
|
||||
/// This type represents the signed content part of an RPKI Certificate.
|
||||
pub struct RpkiTbsCertificate {
|
||||
|
||||
// The General structure is documented in section 4.1 or RFC5280
|
||||
//
|
||||
// TBSCertificate ::= SEQUENCE {
|
||||
@@ -77,7 +65,6 @@ pub struct RpkiTbsCertificate {
|
||||
/// # Encoding
|
||||
///
|
||||
impl RpkiTbsCertificate {
|
||||
|
||||
/// Encodes this certificate.
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
match self.extensions {
|
||||
@@ -86,23 +73,21 @@ impl RpkiTbsCertificate {
|
||||
(
|
||||
Constructed::new(
|
||||
Tag::CTX_0,
|
||||
2.encode() // Version 3 is encoded as 2
|
||||
2.encode(), // Version 3 is encoded as 2
|
||||
),
|
||||
self.serial_number.encode(),
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
self.issuer.encode_ref()
|
||||
self.issuer.encode_ref(),
|
||||
),
|
||||
(
|
||||
self.validity.encode(),
|
||||
self.subject.encode_ref(),
|
||||
self.subject_public_key_info.clone().encode(),
|
||||
id_ext.encode()
|
||||
)
|
||||
id_ext.encode(),
|
||||
),
|
||||
))
|
||||
},
|
||||
RpkiTbsExtension::ResourceExtensions(ref _ext) => {
|
||||
unimplemented!()
|
||||
}
|
||||
RpkiTbsExtension::ResourceExtensions(ref _ext) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +101,7 @@ impl RpkiTbsCertificate {
|
||||
validity: Validity,
|
||||
subject: Name,
|
||||
subject_public_key_info: PublicKey,
|
||||
extensions: RpkiTbsExtension
|
||||
extensions: RpkiTbsExtension,
|
||||
) -> Self {
|
||||
Self {
|
||||
serial_number,
|
||||
@@ -124,7 +109,7 @@ impl RpkiTbsCertificate {
|
||||
validity,
|
||||
subject,
|
||||
subject_public_key_info,
|
||||
extensions
|
||||
extensions,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,9 +139,8 @@ impl IdCertBuilder {
|
||||
duration: ::chrono::Duration,
|
||||
issuing_key: &PublicKey,
|
||||
subject_key: &PublicKey,
|
||||
ext: IdExtensions
|
||||
) -> RpkiTbsCertificate
|
||||
{
|
||||
ext: IdExtensions,
|
||||
) -> RpkiTbsCertificate {
|
||||
let issuer = Name::from_pub_key(issuing_key);
|
||||
let validity = Validity::from_duration(duration);
|
||||
let subject = Name::from_pub_key(subject_key);
|
||||
@@ -167,7 +151,7 @@ impl IdCertBuilder {
|
||||
validity,
|
||||
subject,
|
||||
subject_public_key_info: subject_key.clone(),
|
||||
extensions: RpkiTbsExtension::IdExtensions(ext)
|
||||
extensions: RpkiTbsExtension::IdExtensions(ext),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,18 +159,12 @@ impl IdCertBuilder {
|
||||
issuing_key: &S::KeyId,
|
||||
subject_key: &PublicKey,
|
||||
ext: IdExtensions,
|
||||
signer: &S
|
||||
signer: &S,
|
||||
) -> Result<IdCert, SigningError<S::Error>> {
|
||||
let issuing_key_info = signer.get_key_info(issuing_key)?;
|
||||
let dur = ::chrono::Duration::weeks(52000);
|
||||
|
||||
let tbs = Self::make_tbs_certificate_request(
|
||||
1,
|
||||
dur,
|
||||
&issuing_key_info,
|
||||
&subject_key,
|
||||
ext
|
||||
);
|
||||
let tbs = Self::make_tbs_certificate_request(1, dur, &issuing_key_info, &subject_key, ext);
|
||||
|
||||
let enc_cert = tbs.encode();
|
||||
let enc_cert_c = enc_cert.to_captured(Mode::Der);
|
||||
@@ -194,20 +172,18 @@ impl IdCertBuilder {
|
||||
|
||||
let signature = BitString::new(
|
||||
0,
|
||||
signer.sign(
|
||||
issuing_key,
|
||||
SignatureAlgorithm::default(),
|
||||
enc_cert_b
|
||||
)?.value().clone()
|
||||
signer
|
||||
.sign(issuing_key, SignatureAlgorithm::default(), enc_cert_b)?
|
||||
.value()
|
||||
.clone(),
|
||||
);
|
||||
|
||||
let captured_cert = encode::sequence (
|
||||
(
|
||||
enc_cert,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode()
|
||||
)
|
||||
).to_captured(Mode::Der);
|
||||
let captured_cert = encode::sequence((
|
||||
enc_cert,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode(),
|
||||
))
|
||||
.to_captured(Mode::Der);
|
||||
|
||||
// Todo -> Return the bytes, or a captured, not a parsed cert
|
||||
let id_cert = IdCert::decode(captured_cert.as_ref()).unwrap();
|
||||
@@ -221,80 +197,57 @@ impl IdCertBuilder {
|
||||
/// component.
|
||||
pub fn new_ta_id_cert<S: Signer>(
|
||||
issuing_key: &S::KeyId,
|
||||
signer: &S
|
||||
signer: &S,
|
||||
) -> Result<IdCert, Error<S::Error>> {
|
||||
let issuing_key_info = signer.get_key_info(issuing_key)?;
|
||||
let ext = IdExtensions::for_id_ta_cert(&issuing_key_info);
|
||||
let cert = IdCertBuilder::create_signed_cert(
|
||||
issuing_key,
|
||||
&issuing_key_info,
|
||||
ext,
|
||||
signer
|
||||
)?;
|
||||
let cert = IdCertBuilder::create_signed_cert(issuing_key, &issuing_key_info, ext, signer)?;
|
||||
Ok(cert)
|
||||
}
|
||||
|
||||
pub fn new_ee_cert<S: Signer>(
|
||||
issuing_key: &S::KeyId,
|
||||
subject_key: &PublicKey,
|
||||
signer: &S
|
||||
signer: &S,
|
||||
) -> Result<IdCert, Error<S::Error>> {
|
||||
let issuing_key_info = signer.get_key_info(issuing_key)?;
|
||||
|
||||
let ext = IdExtensions::for_id_ee_cert(
|
||||
subject_key,
|
||||
&issuing_key_info
|
||||
);
|
||||
let ext = IdExtensions::for_id_ee_cert(subject_key, &issuing_key_info);
|
||||
|
||||
let cert = IdCertBuilder::create_signed_cert(
|
||||
issuing_key,
|
||||
subject_key,
|
||||
ext,
|
||||
signer
|
||||
)?;
|
||||
let cert = IdCertBuilder::create_signed_cert(issuing_key, subject_key, ext, signer)?;
|
||||
Ok(cert)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ SignedMessageBuilder ------------------------------------------
|
||||
|
||||
pub struct SignedMessageBuilder {
|
||||
content: OctetString,
|
||||
signer_info: SignedSignerInfo,
|
||||
ee_cert: IdCert,
|
||||
crl: Crl
|
||||
crl: Crl,
|
||||
}
|
||||
|
||||
impl SignedMessageBuilder {
|
||||
pub fn create<S: Signer>(
|
||||
issuing_key: &S::KeyId,
|
||||
signer: &S,
|
||||
message: Bytes
|
||||
message: Bytes,
|
||||
) -> Result<SignedMessageBuilder, Error<S::Error>> {
|
||||
let content = OctetString::new(message);
|
||||
|
||||
let signer_info = SignerInfoBuilder::create(
|
||||
signer,
|
||||
&content.to_bytes()
|
||||
)?;
|
||||
let signer_info = SignerInfoBuilder::create(signer, &content.to_bytes())?;
|
||||
|
||||
let ee_cert = IdCertBuilder::new_ee_cert(
|
||||
issuing_key,
|
||||
signer_info.one_off_key(),
|
||||
signer
|
||||
)?;
|
||||
let ee_cert = IdCertBuilder::new_ee_cert(issuing_key, signer_info.one_off_key(), signer)?;
|
||||
|
||||
let crl = CrlBuilder::create(issuing_key, signer)?;
|
||||
|
||||
Ok(
|
||||
SignedMessageBuilder {
|
||||
content,
|
||||
signer_info,
|
||||
ee_cert,
|
||||
crl
|
||||
}
|
||||
)
|
||||
Ok(SignedMessageBuilder {
|
||||
content,
|
||||
signer_info,
|
||||
ee_cert,
|
||||
crl,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Bytes {
|
||||
@@ -302,7 +255,6 @@ impl SignedMessageBuilder {
|
||||
}
|
||||
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
|
||||
// ContentInfo ::= SEQUENCE {
|
||||
// contentType ContentType,
|
||||
// content [0] EXPLICIT ANY DEFINED BY contentType }
|
||||
@@ -324,57 +276,32 @@ impl SignedMessageBuilder {
|
||||
// The eContentType for the RPKI Protocol Message object is defined as
|
||||
// id-ct-xml, and has the numerical value of 1.2.840.113549.1.9.16.1.28.
|
||||
|
||||
let digest_algorithms = encode::set(
|
||||
encode::sequence(
|
||||
rpki::oid::SHA256.encode()
|
||||
)
|
||||
);
|
||||
let digest_algorithms = encode::set(encode::sequence(rpki::oid::SHA256.encode()));
|
||||
|
||||
let encap_content_info = encode::sequence(
|
||||
(
|
||||
oid::PROTOCOL_CONTENT_TYPE.encode(),
|
||||
Constructed::new(Tag::CTX_0, self.content.clone().encode())
|
||||
)
|
||||
);
|
||||
let encap_content_info = encode::sequence((
|
||||
oid::PROTOCOL_CONTENT_TYPE.encode(),
|
||||
Constructed::new(Tag::CTX_0, self.content.clone().encode()),
|
||||
));
|
||||
|
||||
let certificates = Constructed::new(
|
||||
Tag::CTX_0,
|
||||
self.ee_cert.encode()
|
||||
);
|
||||
let certificates = Constructed::new(Tag::CTX_0, self.ee_cert.encode());
|
||||
|
||||
let crls = Constructed::new(
|
||||
Tag::CTX_1,
|
||||
self.crl.encode_ref()
|
||||
);
|
||||
let crls = Constructed::new(Tag::CTX_1, self.crl.encode_ref());
|
||||
|
||||
let signer_infos = encode::set(self.signer_info.encode());
|
||||
|
||||
encode::sequence(
|
||||
(
|
||||
oid::SIGNED_DATA.encode(),
|
||||
Constructed::new(
|
||||
Tag::CTX_0,
|
||||
encode::sequence(
|
||||
(
|
||||
(
|
||||
3.encode(),
|
||||
digest_algorithms,
|
||||
encap_content_info
|
||||
),
|
||||
(
|
||||
certificates,
|
||||
crls,
|
||||
signer_infos
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
encode::sequence((
|
||||
oid::SIGNED_DATA.encode(),
|
||||
Constructed::new(
|
||||
Tag::CTX_0,
|
||||
encode::sequence((
|
||||
(3.encode(), digest_algorithms, encap_content_info),
|
||||
(certificates, crls, signer_infos),
|
||||
)),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This type represent Signed Attributes in Signer Info.
|
||||
///
|
||||
/// ```text
|
||||
@@ -393,13 +320,12 @@ impl SignedMessageBuilder {
|
||||
/// See section 2.1.6.4 of RFC 6488 for specifications.
|
||||
/// ```
|
||||
pub struct SignedAttributes {
|
||||
content_type: &'static Oid<& 'static [u8]>,
|
||||
content_type: &'static Oid<&'static [u8]>,
|
||||
digest: OctetString,
|
||||
signing_time: Time
|
||||
signing_time: Time,
|
||||
}
|
||||
|
||||
impl SignedAttributes {
|
||||
|
||||
/// Creates a new SignedAttributes.
|
||||
///
|
||||
/// Needs the content type for this specific kind of CMS (protocol, ROA,
|
||||
@@ -408,11 +334,7 @@ impl SignedAttributes {
|
||||
///
|
||||
/// This implementation will include a signing-time attribute using the
|
||||
/// time that the SignedAttributes was created.
|
||||
pub fn new(
|
||||
content_type: &'static Oid<&'static [u8]>,
|
||||
content: &Bytes
|
||||
) -> Self {
|
||||
|
||||
pub fn new(content_type: &'static Oid<&'static [u8]>, content: &Bytes) -> Self {
|
||||
let content_digest = DigestAlgorithm::default().digest(content);
|
||||
|
||||
let digest = Bytes::from(content_digest.as_ref());
|
||||
@@ -421,70 +343,54 @@ impl SignedAttributes {
|
||||
Self {
|
||||
content_type,
|
||||
digest,
|
||||
signing_time: Time::now()
|
||||
signing_time: Time::now(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the SignedAttributes for inclusion in a CMS.
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
(
|
||||
encode::sequence(
|
||||
(
|
||||
oid::CONTENT_TYPE.encode(),
|
||||
encode::set(
|
||||
self.content_type.encode()
|
||||
)
|
||||
)
|
||||
),
|
||||
encode::sequence (
|
||||
(
|
||||
// This implementation will include a signing-time
|
||||
// attribute using the time that the SignedAttributes
|
||||
// was created.
|
||||
oid::SIGNING_TIME.encode(),
|
||||
encode::set(
|
||||
self.signing_time.encode()
|
||||
)
|
||||
)
|
||||
),
|
||||
encode::sequence(
|
||||
(
|
||||
oid::MESSAGE_DIGEST.encode(),
|
||||
encode::set(
|
||||
self.digest.clone().encode()
|
||||
)
|
||||
)
|
||||
)
|
||||
encode::sequence((
|
||||
oid::CONTENT_TYPE.encode(),
|
||||
encode::set(self.content_type.encode()),
|
||||
)),
|
||||
encode::sequence((
|
||||
// This implementation will include a signing-time
|
||||
// attribute using the time that the SignedAttributes
|
||||
// was created.
|
||||
oid::SIGNING_TIME.encode(),
|
||||
encode::set(self.signing_time.encode()),
|
||||
)),
|
||||
encode::sequence((
|
||||
oid::MESSAGE_DIGEST.encode(),
|
||||
encode::set(self.digest.clone().encode()),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates a signature using a one time key
|
||||
pub fn sign<S: Signer>(
|
||||
&self,
|
||||
signer: &S
|
||||
) -> Result<(Signature, PublicKey), Error<S::Error>> {
|
||||
pub fn sign<S: Signer>(&self, signer: &S) -> Result<(Signature, PublicKey), Error<S::Error>> {
|
||||
// See section 5.4 of RFC 5652
|
||||
// ...The IMPLICIT [0] tag in the signedAttrs is not used for the DER
|
||||
// encoding, rather an EXPLICIT SET OF tag is used...
|
||||
let encode_in_set = encode::set(self.encode()).to_captured(Mode::Der);
|
||||
signer.sign_one_off(SignatureAlgorithm::default(), encode_in_set.as_slice())
|
||||
signer
|
||||
.sign_one_off(SignatureAlgorithm::default(), encode_in_set.as_slice())
|
||||
.map_err(Error::SignerError)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------ SignedSignerInfo ----------------------------------------------
|
||||
|
||||
pub struct SignedSignerInfo {
|
||||
signed_attributes: SignedAttributes,
|
||||
key: PublicKey,
|
||||
key_id: KeyIdentifier,
|
||||
signature: OctetString
|
||||
signature: OctetString,
|
||||
}
|
||||
|
||||
impl SignedSignerInfo {
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
// SignerInfo ::= SEQUENCE {
|
||||
// version CMSVersion,
|
||||
// sid SignerIdentifier,
|
||||
@@ -510,25 +416,15 @@ impl SignedSignerInfo {
|
||||
// it seems this MUST NOT include the explicit NULL here
|
||||
let digest_algo = encode::sequence(oid::SHA256.encode());
|
||||
|
||||
let signed_attrs = Constructed::new(
|
||||
Tag::CTX_0,
|
||||
self.signed_attributes.encode()
|
||||
);
|
||||
let signed_attrs = Constructed::new(Tag::CTX_0, self.signed_attributes.encode());
|
||||
|
||||
encode::sequence(
|
||||
encode::sequence((
|
||||
(version, sid, digest_algo, signed_attrs),
|
||||
(
|
||||
(
|
||||
version,
|
||||
sid,
|
||||
digest_algo,
|
||||
signed_attrs
|
||||
),
|
||||
(
|
||||
SignatureAlgorithm::default().cms_encode(),
|
||||
self.signature.clone().encode()
|
||||
)
|
||||
)
|
||||
)
|
||||
SignatureAlgorithm::default().cms_encode(),
|
||||
self.signature.clone().encode(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn one_off_key(&self) -> &PublicKey {
|
||||
@@ -536,8 +432,6 @@ impl SignedSignerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ SignerInfoBuilder ---------------------------------------------
|
||||
|
||||
pub struct SignerInfoBuilder;
|
||||
@@ -553,12 +447,11 @@ impl SignerInfoBuilder {
|
||||
/// really only require some bits.
|
||||
pub fn create<S: Signer>(
|
||||
signer: &S,
|
||||
message: &Bytes
|
||||
message: &Bytes,
|
||||
) -> Result<SignedSignerInfo, Error<S::Error>> {
|
||||
|
||||
let signed_attributes = SignedAttributes::new(
|
||||
&oid::PROTOCOL_CONTENT_TYPE, // XXX TODO: derive from message
|
||||
message
|
||||
message,
|
||||
);
|
||||
|
||||
let (signature, key) = signed_attributes.sign(signer)?;
|
||||
@@ -566,27 +459,20 @@ impl SignerInfoBuilder {
|
||||
let key_id = KeyIdentifier::new(&key);
|
||||
let signature = OctetString::new(signature.value().clone());
|
||||
|
||||
Ok(
|
||||
SignedSignerInfo {
|
||||
signed_attributes,
|
||||
key,
|
||||
key_id,
|
||||
signature
|
||||
}
|
||||
)
|
||||
Ok(SignedSignerInfo {
|
||||
signed_attributes,
|
||||
key,
|
||||
key_id,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//------------ CrlBuilder ----------------------------------------------------
|
||||
|
||||
pub struct CrlBuilder;
|
||||
|
||||
impl CrlBuilder {
|
||||
|
||||
/// Creates a CRL for use with protocol messages. I.e. it revokes nothing,
|
||||
/// because smart people use single use keys for EE certs, and it's valid
|
||||
/// for, like, forever -- cause really this thing is useless. Still it is
|
||||
@@ -594,61 +480,51 @@ impl CrlBuilder {
|
||||
///
|
||||
/// This will all be changed in future when we implement generating CRLs
|
||||
/// for the RPKI CA.
|
||||
pub fn create<S: Signer>(
|
||||
issuing_key: &S::KeyId,
|
||||
signer: &S
|
||||
) -> Result<Crl, Error<S::Error>>
|
||||
{
|
||||
pub fn create<S: Signer>(issuing_key: &S::KeyId, signer: &S) -> Result<Crl, Error<S::Error>> {
|
||||
let pub_key = signer.get_key_info(issuing_key)?;
|
||||
let name = Name::from_pub_key(&pub_key);
|
||||
let now = Time::new(Utc::now());
|
||||
let eternity = Time::new(Utc::now()+::chrono::Duration::weeks(52000));
|
||||
let eternity = Time::new(Utc::now() + ::chrono::Duration::weeks(52000));
|
||||
|
||||
let crl_number = CrlNumber::new(1);
|
||||
let aki = AuthorityKeyIdentifier::new(&pub_key);
|
||||
|
||||
let extensions = Constructed::new(
|
||||
Tag::CTX_0,
|
||||
encode::sequence(
|
||||
(
|
||||
aki.encode(),
|
||||
crl_number.encode()
|
||||
)
|
||||
)
|
||||
encode::sequence((aki.encode(), crl_number.encode())),
|
||||
);
|
||||
|
||||
let crl_data = encode::sequence(
|
||||
let crl_data = encode::sequence((
|
||||
(
|
||||
(
|
||||
1.encode(),
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
name.encode_ref()
|
||||
),
|
||||
(
|
||||
now.encode(),
|
||||
eternity.encode(),
|
||||
// Real revocations go here
|
||||
extensions
|
||||
)
|
||||
)
|
||||
);
|
||||
1.encode(),
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
name.encode_ref(),
|
||||
),
|
||||
(
|
||||
now.encode(),
|
||||
eternity.encode(),
|
||||
// Real revocations go here
|
||||
extensions,
|
||||
),
|
||||
));
|
||||
|
||||
let signature = BitString::new(
|
||||
0,
|
||||
signer.sign(
|
||||
issuing_key,
|
||||
SignatureAlgorithm::default(),
|
||||
crl_data.to_captured(Mode::Der).as_slice()
|
||||
)?.value().clone()
|
||||
signer
|
||||
.sign(
|
||||
issuing_key,
|
||||
SignatureAlgorithm::default(),
|
||||
crl_data.to_captured(Mode::Der).as_slice(),
|
||||
)?
|
||||
.value()
|
||||
.clone(),
|
||||
);
|
||||
|
||||
let crl_obj = encode::sequence(
|
||||
(
|
||||
crl_data,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode()
|
||||
)
|
||||
);
|
||||
let crl_obj = encode::sequence((
|
||||
crl_data,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode(),
|
||||
));
|
||||
|
||||
let crl = Crl::decode(crl_obj.to_captured(Mode::Der).as_ref())?;
|
||||
|
||||
@@ -656,7 +532,6 @@ impl CrlBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error<S: fmt::Debug + fmt::Display> {
|
||||
#[display(fmt = "{}", _0)]
|
||||
@@ -676,15 +551,21 @@ pub enum Error<S: fmt::Debug + fmt::Display> {
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug + fmt::Display> From<KeyError<S>> for Error<S> {
|
||||
fn from(e: KeyError<S>) -> Self { Error::KeyError(e) }
|
||||
fn from(e: KeyError<S>) -> Self {
|
||||
Error::KeyError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug + fmt::Display> From<SigningError<S>> for Error<S> {
|
||||
fn from(e: SigningError<S>) -> Self { Error::SigningError(e) }
|
||||
fn from(e: SigningError<S>) -> Self {
|
||||
Error::SigningError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug + fmt::Display> From<decode::Error> for Error<S> {
|
||||
fn from(e: decode::Error) -> Self { Error::DecodeError(e) }
|
||||
fn from(e: decode::Error) -> Self {
|
||||
Error::DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
@@ -695,13 +576,13 @@ pub mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::util::test;
|
||||
use signing::softsigner::OpenSslSigner;
|
||||
use signing::PublicKeyAlgorithm;
|
||||
use remote::sigmsg::SignedMessage;
|
||||
use publication::query::ListQuery;
|
||||
use util::softsigner::OpenSslSigner;
|
||||
use remote::publication::query::ListQuery;
|
||||
use remote::rfc8181::ListQuery;
|
||||
use remote::sigmsg::SignedMessage;
|
||||
use signing::softsigner::OpenSslSigner;
|
||||
use signing::PublicKeyAlgorithm;
|
||||
use util::softsigner::OpenSslSigner;
|
||||
|
||||
#[test]
|
||||
fn should_create_self_signed_ta_id_cert() {
|
||||
@@ -709,7 +590,7 @@ pub mod tests {
|
||||
let mut s = OpenSslSigner::build(&d);
|
||||
let key_id = s.create_key(&PublicKeyAlgorithm::RsaEncryption).unwrap();
|
||||
|
||||
let id_cert = IdCertBuilder::new_ta_id_cert(&key_id, & mut s).unwrap();
|
||||
let id_cert = IdCertBuilder::new_ta_id_cert(&key_id, &mut s).unwrap();
|
||||
id_cert.validate_ta().unwrap();
|
||||
});
|
||||
}
|
||||
@@ -721,7 +602,7 @@ pub mod tests {
|
||||
let key_id = s.create_key(&PublicKeyAlgorithm::RsaEncryption).unwrap();
|
||||
let key_info = s.get_key_info(&key_id).unwrap();
|
||||
|
||||
let crl = CrlBuilder::create(&key_id, & mut s).unwrap();
|
||||
let crl = CrlBuilder::create(&key_id, &mut s).unwrap();
|
||||
crl.validate(&key_info).unwrap();
|
||||
})
|
||||
}
|
||||
@@ -731,15 +612,11 @@ pub mod tests {
|
||||
test::test_with_tmp_dir(|d| {
|
||||
let mut s = OpenSslSigner::build(&d);
|
||||
let key_id = s.create_key(&PublicKeyAlgorithm::RsaEncryption).unwrap();
|
||||
let id_cert = IdCertBuilder::new_ta_id_cert(&key_id, & mut s).unwrap();
|
||||
let id_cert = IdCertBuilder::new_ta_id_cert(&key_id, &mut s).unwrap();
|
||||
|
||||
let message = ListQuery::build_message();
|
||||
|
||||
let builder = SignedMessageBuilder::create(
|
||||
&key_id,
|
||||
&mut s,
|
||||
message.clone()
|
||||
).unwrap();
|
||||
let builder = SignedMessageBuilder::create(&key_id, &mut s, message.clone()).unwrap();
|
||||
|
||||
let encoded_cms = builder.encode().to_captured(Mode::Der);
|
||||
|
||||
@@ -752,7 +629,4 @@ pub mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::api::admin::Handle;
|
||||
use crate::eventsourcing::{
|
||||
Aggregate,
|
||||
Command,
|
||||
CommandDetails,
|
||||
Event,
|
||||
SentCommand,
|
||||
StoredEvent
|
||||
};
|
||||
use crate::remote::api::{
|
||||
ClientAuth,
|
||||
ClientInfo
|
||||
};
|
||||
use crate::eventsourcing::{Aggregate, Command, CommandDetails, Event, SentCommand, StoredEvent};
|
||||
use crate::remote::api::{ClientAuth, ClientInfo};
|
||||
use crate::remote::id::IdCert;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// const fn is not stable yet
|
||||
const ID: &str = "cms-clients";
|
||||
pub fn id() -> Handle { Handle::from(ID) }
|
||||
|
||||
|
||||
pub fn id() -> Handle {
|
||||
Handle::from(ID)
|
||||
}
|
||||
|
||||
//------------ ClientsEvents --------------------------------------------
|
||||
|
||||
@@ -30,11 +20,19 @@ impl ClientsEvents {
|
||||
}
|
||||
|
||||
pub fn added_client(version: u64, handle: Handle, client: ClientAuth) -> ClientsEvent {
|
||||
StoredEvent::new(&id(), version, ClientsEventDetails::AddedClient(handle, client))
|
||||
StoredEvent::new(
|
||||
&id(),
|
||||
version,
|
||||
ClientsEventDetails::AddedClient(handle, client),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn updated_cert(version: u64, handle: Handle, cert: IdCert) -> ClientsEvent {
|
||||
StoredEvent::new(&id(), version, ClientsEventDetails::UpdatedClientCert(handle, cert))
|
||||
StoredEvent::new(
|
||||
&id(),
|
||||
version,
|
||||
ClientsEventDetails::UpdatedClientCert(handle, cert),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn removed_client(version: u64, handle: Handle) -> ClientsEvent {
|
||||
@@ -52,22 +50,29 @@ pub type ClientsInit = StoredEvent<ClientsInitDetails>;
|
||||
pub enum ClientsEventDetails {
|
||||
AddedClient(Handle, ClientAuth),
|
||||
UpdatedClientCert(Handle, IdCert),
|
||||
RemovedClient(Handle)
|
||||
RemovedClient(Handle),
|
||||
}
|
||||
|
||||
pub type ClientsEvent = StoredEvent<ClientsEventDetails>;
|
||||
|
||||
|
||||
//------------ ClientsCommands -------------------------------------------
|
||||
|
||||
pub struct ClientsCommands;
|
||||
|
||||
impl ClientsCommands {
|
||||
pub fn add(handle: Handle, client: ClientAuth) -> ClientsCommand {
|
||||
SentCommand::new(&id(), None, ClientsCommandDetails::AddClient(handle, client))
|
||||
SentCommand::new(
|
||||
&id(),
|
||||
None,
|
||||
ClientsCommandDetails::AddClient(handle, client),
|
||||
)
|
||||
}
|
||||
pub fn update_cert(handle: Handle, cert: IdCert) -> ClientsCommand {
|
||||
SentCommand::new(&id(), None, ClientsCommandDetails::UpdateClientCert(handle, cert))
|
||||
SentCommand::new(
|
||||
&id(),
|
||||
None,
|
||||
ClientsCommandDetails::UpdateClientCert(handle, cert),
|
||||
)
|
||||
}
|
||||
pub fn remove(handle: Handle) -> ClientsCommand {
|
||||
SentCommand::new(&id(), None, ClientsCommandDetails::RemoveClient(handle))
|
||||
@@ -79,7 +84,7 @@ impl ClientsCommands {
|
||||
pub enum ClientsCommandDetails {
|
||||
AddClient(Handle, ClientAuth),
|
||||
UpdateClientCert(Handle, IdCert),
|
||||
RemoveClient(Handle)
|
||||
RemoveClient(Handle),
|
||||
}
|
||||
|
||||
impl CommandDetails for ClientsCommandDetails {
|
||||
@@ -88,7 +93,6 @@ impl CommandDetails for ClientsCommandDetails {
|
||||
|
||||
pub type ClientsCommand = SentCommand<ClientsCommandDetails>;
|
||||
|
||||
|
||||
//------------ ClientManager -------------------------------------------------
|
||||
|
||||
/// This type manages the known clients for the CMS proxy server. I.e. it
|
||||
@@ -101,10 +105,9 @@ pub struct ClientManager {
|
||||
version: u64,
|
||||
|
||||
// Clients known by this proxy
|
||||
clients: HashMap<Handle, ClientAuth>
|
||||
clients: HashMap<Handle, ClientAuth>,
|
||||
}
|
||||
|
||||
|
||||
impl Aggregate for ClientManager {
|
||||
type Command = ClientsCommand;
|
||||
type Event = ClientsEvent;
|
||||
@@ -131,12 +134,12 @@ impl Aggregate for ClientManager {
|
||||
)
|
||||
}
|
||||
match event.into_details() {
|
||||
ClientsEventDetails::AddedClient(handle, client) => {
|
||||
ClientsEventDetails::AddedClient(handle, client) => {
|
||||
self.clients.insert(handle, client);
|
||||
},
|
||||
}
|
||||
ClientsEventDetails::UpdatedClientCert(handle, id_cert) => {
|
||||
self.clients.get_mut(&handle).unwrap().set_cert(id_cert);
|
||||
},
|
||||
}
|
||||
ClientsEventDetails::RemovedClient(handle) => {
|
||||
self.clients.remove(&handle);
|
||||
}
|
||||
@@ -148,7 +151,7 @@ impl Aggregate for ClientManager {
|
||||
fn process_command(&self, command: Self::Command) -> Result<Vec<Self::Event>, Self::Error> {
|
||||
if let Some(version) = command.version() {
|
||||
if version != self.version {
|
||||
return Err(Error::ConcurrentModification(version, self.version))
|
||||
return Err(Error::ConcurrentModification(version, self.version));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,11 +161,11 @@ impl Aggregate for ClientManager {
|
||||
ClientsCommandDetails::AddClient(handle, client) => {
|
||||
self.assert_new(&handle)?;
|
||||
res.push(ClientsEvents::added_client(self.version, handle, client))
|
||||
},
|
||||
}
|
||||
ClientsCommandDetails::UpdateClientCert(handle, cert) => {
|
||||
self.assert_exists(&handle)?;
|
||||
res.push(ClientsEvents::updated_cert(self.version, handle, cert))
|
||||
},
|
||||
}
|
||||
ClientsCommandDetails::RemoveClient(handle) => {
|
||||
self.assert_exists(&handle)?;
|
||||
res.push(ClientsEvents::removed_client(self.version, handle))
|
||||
@@ -181,8 +184,8 @@ impl ClientManager {
|
||||
pub fn list(&self) -> Vec<ClientInfo> {
|
||||
let mut res = vec![];
|
||||
for (handle, auth) in self.clients.iter() {
|
||||
res.push(ClientInfo::new(handle.clone(), auth.clone()));
|
||||
};
|
||||
res.push(ClientInfo::new(handle.clone(), auth.clone()));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
@@ -193,20 +196,22 @@ impl ClientManager {
|
||||
fn assert_new(&self, handle: &Handle) -> ProxyResult<()> {
|
||||
if self.has_client(handle) {
|
||||
Err(Error::ClientExists(handle.clone()))
|
||||
} else { Ok(()) }
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn assert_exists(&self, handle: &Handle) -> ProxyResult<()> {
|
||||
if ! self.has_client(handle) {
|
||||
if !self.has_client(handle) {
|
||||
Err(Error::NoClient(handle.clone()))
|
||||
} else { Ok(()) }
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ProxyResult<T> = Result<T, Error>;
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -229,14 +234,14 @@ impl std::error::Error for Error {}
|
||||
pub mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use crate::util::test;
|
||||
use crate::eventsourcing::AggregateStore;
|
||||
use crate::eventsourcing::DiskAggregateStore;
|
||||
use crate::util::softsigner::OpenSslSigner;
|
||||
use crate::remote::builder::IdCertBuilder;
|
||||
use crate::util::softsigner::OpenSslSigner;
|
||||
use crate::util::test;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn new_id_cert(work_dir: &PathBuf) -> IdCert {
|
||||
let mut s = OpenSslSigner::build(work_dir).unwrap();
|
||||
@@ -248,17 +253,12 @@ pub mod tests {
|
||||
let cert = new_id_cert(work_dir);
|
||||
let handle = Handle::from(name);
|
||||
|
||||
ClientsCommands::add(
|
||||
handle,
|
||||
ClientAuth::new(cert)
|
||||
)
|
||||
ClientsCommands::add(handle, ClientAuth::new(cert))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn should_manage_clients() {
|
||||
test::test_under_tmp(|d| {
|
||||
|
||||
// Set up a store for the proxy
|
||||
let store = DiskAggregateStore::<ClientManager>::new(&d, "proxy").unwrap();
|
||||
|
||||
@@ -274,10 +274,8 @@ pub mod tests {
|
||||
let alice_cert1 = new_id_cert(&d);
|
||||
let alice_handle = Handle::from("alice");
|
||||
|
||||
let add_alice = ClientsCommands::add(
|
||||
alice_handle.clone(),
|
||||
ClientAuth::new(alice_cert1.clone())
|
||||
);
|
||||
let add_alice =
|
||||
ClientsCommands::add(alice_handle.clone(), ClientAuth::new(alice_cert1.clone()));
|
||||
|
||||
let events = proxy.process_command(add_alice).unwrap();
|
||||
assert_eq!(1, events.len());
|
||||
@@ -299,10 +297,8 @@ pub mod tests {
|
||||
|
||||
// Update cert
|
||||
let alice_cert2 = new_id_cert(&d);
|
||||
let update_alice_cert = ClientsCommands::update_cert(
|
||||
alice_handle.clone(),
|
||||
alice_cert2.clone()
|
||||
);
|
||||
let update_alice_cert =
|
||||
ClientsCommands::update_cert(alice_handle.clone(), alice_cert2.clone());
|
||||
|
||||
let events = proxy.process_command(update_alice_cert).unwrap();
|
||||
assert_eq!(1, events.len());
|
||||
@@ -320,7 +316,6 @@ pub mod tests {
|
||||
let proxy = store.update(&id(), proxy, events).unwrap();
|
||||
|
||||
assert!(proxy.client_auth(&alice_handle).is_none());
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+84
-127
@@ -1,23 +1,18 @@
|
||||
use bcder::{Mode, OctetString, Oid, Tag, Unsigned};
|
||||
use bcder::{decode, encode};
|
||||
use bcder::encode::Values;
|
||||
use bcder::encode::Constructed;
|
||||
use bcder::encode::Values;
|
||||
use bcder::{decode, encode};
|
||||
use bcder::{Mode, OctetString, Oid, Tag, Unsigned};
|
||||
use bytes::Bytes;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use rpki::uri;
|
||||
use rpki::cert::ext::{
|
||||
AuthorityKeyIdentifier,
|
||||
BasicCa,
|
||||
SubjectKeyIdentifier
|
||||
};
|
||||
use rpki::cert::ext::{AuthorityKeyIdentifier, BasicCa, SubjectKeyIdentifier};
|
||||
use rpki::crypto::{PublicKey, SignatureAlgorithm};
|
||||
use rpki::uri;
|
||||
use rpki::x509::{Name, SignedData, Time, ValidationError, Validity};
|
||||
|
||||
use crate::remote::rfc8183::ServiceUri;
|
||||
use crate::util::softsigner::SignerKeyId;
|
||||
|
||||
|
||||
//------------ MyIdentity ----------------------------------------------------
|
||||
|
||||
/// This type stores identity details for a client or server involved in RPKI
|
||||
@@ -28,7 +23,7 @@ pub struct MyIdentity {
|
||||
|
||||
id_cert: IdCert,
|
||||
|
||||
key_id: SignerKeyId
|
||||
key_id: SignerKeyId,
|
||||
}
|
||||
|
||||
impl MyIdentity {
|
||||
@@ -36,7 +31,7 @@ impl MyIdentity {
|
||||
MyIdentity {
|
||||
name: name.to_string(),
|
||||
id_cert,
|
||||
key_id
|
||||
key_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,15 +54,14 @@ impl MyIdentity {
|
||||
|
||||
impl PartialEq for MyIdentity {
|
||||
fn eq(&self, other: &MyIdentity) -> bool {
|
||||
self.name == other.name &&
|
||||
self.id_cert.to_bytes() == other.id_cert.to_bytes() &&
|
||||
self.key_id == other.key_id
|
||||
self.name == other.name
|
||||
&& self.id_cert.to_bytes() == other.id_cert.to_bytes()
|
||||
&& self.key_id == other.key_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MyIdentity {}
|
||||
|
||||
|
||||
//------------ ParentInfo ----------------------------------------------------
|
||||
|
||||
/// This type stores details about a parent publication server: in
|
||||
@@ -80,11 +74,7 @@ pub struct ParentInfo {
|
||||
}
|
||||
|
||||
impl ParentInfo {
|
||||
pub fn new(
|
||||
publisher_handle: String,
|
||||
id_cert: IdCert,
|
||||
service_uri: ServiceUri,
|
||||
) -> Self {
|
||||
pub fn new(publisher_handle: String, id_cert: IdCert, service_uri: ServiceUri) -> Self {
|
||||
ParentInfo {
|
||||
publisher_handle,
|
||||
id_cert,
|
||||
@@ -110,15 +100,14 @@ impl ParentInfo {
|
||||
|
||||
impl PartialEq for ParentInfo {
|
||||
fn eq(&self, other: &ParentInfo) -> bool {
|
||||
self.id_cert.to_bytes() == other.id_cert.to_bytes() &&
|
||||
self.service_uri == other.service_uri &&
|
||||
self.publisher_handle == other.publisher_handle
|
||||
self.id_cert.to_bytes() == other.id_cert.to_bytes()
|
||||
&& self.service_uri == other.service_uri
|
||||
&& self.publisher_handle == other.publisher_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ParentInfo {}
|
||||
|
||||
|
||||
//------------ MyRepoInfo ----------------------------------------------------
|
||||
|
||||
/// This type stores details about the repository URIs available to a
|
||||
@@ -126,15 +115,15 @@ impl Eq for ParentInfo {}
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct MyRepoInfo {
|
||||
sia_base: uri::Rsync,
|
||||
notify_sia: uri::Https
|
||||
notify_sia: uri::Https,
|
||||
}
|
||||
|
||||
impl MyRepoInfo {
|
||||
pub fn new(
|
||||
sia_base: uri::Rsync,
|
||||
notify_sia: uri::Https
|
||||
) -> Self {
|
||||
MyRepoInfo { sia_base, notify_sia }
|
||||
pub fn new(sia_base: uri::Rsync, notify_sia: uri::Https) -> Self {
|
||||
MyRepoInfo {
|
||||
sia_base,
|
||||
notify_sia,
|
||||
}
|
||||
}
|
||||
|
||||
/// The base rsync directory under which the publisher may publish.
|
||||
@@ -150,14 +139,12 @@ impl MyRepoInfo {
|
||||
|
||||
impl PartialEq for MyRepoInfo {
|
||||
fn eq(&self, other: &MyRepoInfo) -> bool {
|
||||
self.sia_base == other.sia_base &&
|
||||
self.notify_sia == other.notify_sia
|
||||
self.sia_base == other.sia_base && self.notify_sia == other.notify_sia
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MyRepoInfo {}
|
||||
|
||||
|
||||
//------------ IdCert --------------------------------------------------------
|
||||
|
||||
/// An Identity Certificate.
|
||||
@@ -246,40 +233,40 @@ impl IdCert {
|
||||
}
|
||||
|
||||
/// Takes an encoded certificate from the beginning of a value.
|
||||
pub fn take_from<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
) -> Result<Self, S::Err> {
|
||||
pub fn take_from<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
|
||||
cons.take_sequence(Self::from_constructed)
|
||||
}
|
||||
|
||||
/// Parses the content of a Certificate sequence.
|
||||
pub fn from_constructed<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
cons: &mut decode::Constructed<S>,
|
||||
) -> Result<Self, S::Err> {
|
||||
let signed_data = SignedData::from_constructed(cons)?;
|
||||
|
||||
signed_data.data().clone().decode(|cons| {
|
||||
cons.take_sequence(|cons| {
|
||||
// version [0] EXPLICIT Version DEFAULT v1.
|
||||
// -- we need extensions so apparently, we want v3 which,
|
||||
// confusingly, is 2.
|
||||
cons.take_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(2))?;
|
||||
signed_data
|
||||
.data()
|
||||
.clone()
|
||||
.decode(|cons| {
|
||||
cons.take_sequence(|cons| {
|
||||
// version [0] EXPLICIT Version DEFAULT v1.
|
||||
// -- we need extensions so apparently, we want v3 which,
|
||||
// confusingly, is 2.
|
||||
cons.take_constructed_if(Tag::CTX_0, |c| c.skip_u8_if(2))?;
|
||||
|
||||
Ok(IdCert {
|
||||
signed_data,
|
||||
serial_number: Unsigned::take_from(cons)?,
|
||||
signature: SignatureAlgorithm::x509_take_from(cons)?,
|
||||
issuer: Name::take_from(cons)?,
|
||||
validity: Validity::take_from(cons)?,
|
||||
subject: Name::take_from(cons)?,
|
||||
subject_public_key_info: PublicKey::take_from(cons)?,
|
||||
extensions: cons.take_constructed_if(
|
||||
Tag::CTX_3,
|
||||
IdExtensions::take_from
|
||||
)?,
|
||||
Ok(IdCert {
|
||||
signed_data,
|
||||
serial_number: Unsigned::take_from(cons)?,
|
||||
signature: SignatureAlgorithm::x509_take_from(cons)?,
|
||||
issuer: Name::take_from(cons)?,
|
||||
validity: Validity::take_from(cons)?,
|
||||
subject: Name::take_from(cons)?,
|
||||
subject_public_key_info: PublicKey::take_from(cons)?,
|
||||
extensions: cons
|
||||
.take_constructed_if(Tag::CTX_3, IdExtensions::take_from)?,
|
||||
})
|
||||
})
|
||||
})
|
||||
}).map_err(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
@@ -316,7 +303,8 @@ impl IdCert {
|
||||
}
|
||||
|
||||
// Verify that this is self signed
|
||||
self.signed_data.verify_signature(&self.subject_public_key_info)?;
|
||||
self.signed_data
|
||||
.verify_signature(&self.subject_public_key_info)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -327,25 +315,18 @@ impl IdCert {
|
||||
/// by the provided `issuer` certificate.
|
||||
///
|
||||
/// Note that this does _not_ check the CRL.
|
||||
pub fn validate_ee(
|
||||
&self,
|
||||
issuer: &IdCert,
|
||||
) -> Result<(), ValidationError> {
|
||||
pub fn validate_ee(&self, issuer: &IdCert) -> Result<(), ValidationError> {
|
||||
self.validate_ee_at(issuer, Time::now())
|
||||
}
|
||||
|
||||
pub fn validate_ee_at(
|
||||
&self,
|
||||
issuer: &IdCert,
|
||||
now: Time,
|
||||
) -> Result<(), ValidationError> {
|
||||
pub fn validate_ee_at(&self, issuer: &IdCert, now: Time) -> Result<(), ValidationError> {
|
||||
self.validate_basics(now)?;
|
||||
self.validate_issued(issuer)?;
|
||||
|
||||
// Basic Constraints: Must not be a CA cert.
|
||||
if let Some(basic_ca) = &self.extensions.basic_ca {
|
||||
if basic_ca.ca() {
|
||||
return Err(ValidationError)
|
||||
return Err(ValidationError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +335,6 @@ impl IdCert {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
//--- Validation Components
|
||||
|
||||
/// Validates basic compliance with RFC8183 and RFC6492
|
||||
@@ -369,7 +349,7 @@ impl IdCert {
|
||||
if self.extensions.subject_key_id().as_slice().unwrap()
|
||||
!= self.subject_public_key_info().key_identifier().as_ref()
|
||||
{
|
||||
return Err(ValidationError)
|
||||
return Err(ValidationError);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -383,18 +363,14 @@ impl IdCert {
|
||||
///
|
||||
/// This check assumes for now that we are always dealing with V3
|
||||
/// certificates and AKI and SKI have to match.
|
||||
fn validate_issued(
|
||||
&self,
|
||||
issuer: &IdCert,
|
||||
) -> Result<(), ValidationError> {
|
||||
fn validate_issued(&self, issuer: &IdCert) -> Result<(), ValidationError> {
|
||||
// Authority Key Identifier. Must be present and match the
|
||||
// subject key ID of `issuer`.
|
||||
if let Some(aki) = self.extensions.authority_key_id() {
|
||||
if aki != issuer.extensions.subject_key_id() {
|
||||
return Err(ValidationError)
|
||||
return Err(ValidationError);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return Err(ValidationError);
|
||||
}
|
||||
|
||||
@@ -410,7 +386,7 @@ impl IdCert {
|
||||
// und the “cA” flag must be set (RFC5280).
|
||||
if let Some(ref ca) = self.extensions.basic_ca {
|
||||
if ca.ca() {
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,15 +394,12 @@ impl IdCert {
|
||||
}
|
||||
|
||||
/// Validates the certificate’s signature.
|
||||
fn validate_signature(
|
||||
&self,
|
||||
issuer: &IdCert
|
||||
) -> Result<(), ValidationError> {
|
||||
self.signed_data.verify_signature(issuer.subject_public_key_info())
|
||||
fn validate_signature(&self, issuer: &IdCert) -> Result<(), ValidationError> {
|
||||
self.signed_data
|
||||
.verify_signature(issuer.subject_public_key_info())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--- AsRef
|
||||
|
||||
impl AsRef<IdCert> for IdCert {
|
||||
@@ -436,10 +409,10 @@ impl AsRef<IdCert> for IdCert {
|
||||
}
|
||||
|
||||
impl Serialize for IdCert {
|
||||
fn serialize<S>(
|
||||
&self,
|
||||
serializer: S
|
||||
) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes = self.to_bytes();
|
||||
let str = base64::encode(&bytes);
|
||||
str.serialize(serializer)
|
||||
@@ -455,9 +428,10 @@ impl PartialEq for IdCert {
|
||||
impl Eq for IdCert {}
|
||||
|
||||
impl<'de> Deserialize<'de> for IdCert {
|
||||
fn deserialize<D>(
|
||||
deserializer: D
|
||||
) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de;
|
||||
|
||||
let some = String::deserialize(deserializer)?;
|
||||
@@ -467,8 +441,6 @@ impl<'de> Deserialize<'de> for IdCert {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ IdExtensions --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -489,9 +461,7 @@ pub struct IdExtensions {
|
||||
/// # Decoding
|
||||
///
|
||||
impl IdExtensions {
|
||||
pub fn take_from<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
) -> Result<Self, S::Err> {
|
||||
pub fn take_from<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
|
||||
cons.take_sequence(|cons| {
|
||||
let mut basic_ca = None;
|
||||
let mut subject_key_id = None;
|
||||
@@ -504,13 +474,9 @@ impl IdExtensions {
|
||||
if id == oid::CE_BASIC_CONSTRAINTS {
|
||||
BasicCa::take(content, critical, &mut basic_ca)
|
||||
} else if id == oid::CE_SUBJECT_KEY_IDENTIFIER {
|
||||
SubjectKeyIdentifier::take(
|
||||
content, critical, &mut subject_key_id
|
||||
)
|
||||
SubjectKeyIdentifier::take(content, critical, &mut subject_key_id)
|
||||
} else if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
|
||||
AuthorityKeyIdentifier::take(
|
||||
content, critical, &mut authority_key_id
|
||||
)
|
||||
AuthorityKeyIdentifier::take(content, critical, &mut authority_key_id)
|
||||
} else {
|
||||
// Id Certificates are poorly defined and may
|
||||
// contain critical extensions we do not actually
|
||||
@@ -534,45 +500,38 @@ impl IdExtensions {
|
||||
// We have to do this the hard way because some extensions are optional.
|
||||
// Therefore we need logic to determine which ones to encode.
|
||||
impl IdExtensions {
|
||||
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
Constructed::new(
|
||||
Tag::CTX_3,
|
||||
encode::sequence(
|
||||
(
|
||||
self.basic_ca.as_ref().map(BasicCa::encode),
|
||||
self.subject_key_id.clone().encode(),
|
||||
self.authority_key_id.clone().map(AuthorityKeyIdentifier::encode)
|
||||
)
|
||||
)
|
||||
encode::sequence((
|
||||
self.basic_ca.as_ref().map(BasicCa::encode),
|
||||
self.subject_key_id.clone().encode(),
|
||||
self.authority_key_id
|
||||
.clone()
|
||||
.map(AuthorityKeyIdentifier::encode),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// # Creating
|
||||
///
|
||||
impl IdExtensions {
|
||||
|
||||
/// Creates extensions to be used on a self-signed TA IdCert
|
||||
pub fn for_id_ta_cert(key: &PublicKey) -> Self {
|
||||
IdExtensions{
|
||||
IdExtensions {
|
||||
basic_ca: Some(BasicCa::new(true, true)),
|
||||
subject_key_id: SubjectKeyIdentifier::new(key),
|
||||
authority_key_id: Some(AuthorityKeyIdentifier::new(key))
|
||||
authority_key_id: Some(AuthorityKeyIdentifier::new(key)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates extensions to be used on an EE IdCert in a protocol CMS
|
||||
pub fn for_id_ee_cert(
|
||||
subject_key: &PublicKey,
|
||||
issuing_key: &PublicKey
|
||||
) -> Self {
|
||||
IdExtensions{
|
||||
pub fn for_id_ee_cert(subject_key: &PublicKey, issuing_key: &PublicKey) -> Self {
|
||||
IdExtensions {
|
||||
basic_ca: None,
|
||||
subject_key_id: SubjectKeyIdentifier::new(subject_key),
|
||||
authority_key_id: Some(AuthorityKeyIdentifier::new(issuing_key))
|
||||
authority_key_id: Some(AuthorityKeyIdentifier::new(issuing_key)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,12 +546,11 @@ impl IdExtensions {
|
||||
pub fn authority_key_id(&self) -> Option<&OctetString> {
|
||||
match &self.authority_key_id {
|
||||
Some(a) => Some(a.authority_key_id()),
|
||||
None => None
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OIDs ----------------------------------------------------------
|
||||
|
||||
mod oid {
|
||||
@@ -603,7 +561,6 @@ mod oid {
|
||||
pub const CE_AUTHORITY_KEY_IDENTIFIER: Oid<&[u8]> = Oid(&[85, 29, 35]);
|
||||
}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
// is pub so that we can use a parsed test IdCert for now for testing
|
||||
@@ -620,8 +577,8 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_parse_id_publisher_ta_cert() {
|
||||
test_id_certificate().validate_ta_at(
|
||||
Time::utc(2012, 1, 1, 0, 0, 0)
|
||||
).unwrap();
|
||||
test_id_certificate()
|
||||
.validate_ta_at(Time::utc(2012, 1, 1, 0, 0, 0))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ pub mod clients;
|
||||
pub mod id;
|
||||
pub mod proxy;
|
||||
pub mod responder;
|
||||
pub mod rfc6492;
|
||||
pub mod rfc8181;
|
||||
pub mod rfc8183;
|
||||
pub mod rfc6492;
|
||||
pub mod sigmsg;
|
||||
pub mod sigmsg;
|
||||
|
||||
+114
-146
@@ -2,84 +2,42 @@ use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bcder::{Captured, Mode};
|
||||
use bcder::encode::Values;
|
||||
use bcder::{Captured, Mode};
|
||||
|
||||
use rpki::uri;
|
||||
use rpki::x509::ValidationError;
|
||||
|
||||
use crate::api::{
|
||||
ErrorCode,
|
||||
ErrorResponse,
|
||||
};
|
||||
use crate::api::admin::Handle;
|
||||
use crate::api::publication::{
|
||||
ListReply,
|
||||
PublishRequest,
|
||||
PublishDelta,
|
||||
};
|
||||
use crate::eventsourcing::{
|
||||
Aggregate,
|
||||
AggregateStore,
|
||||
AggregateStoreError,
|
||||
DiskAggregateStore,
|
||||
};
|
||||
use crate::util::httpclient;
|
||||
use crate::util::softsigner::{OpenSslSigner, SignerError};
|
||||
use rpki::crypto::{
|
||||
PublicKeyFormat,
|
||||
Signer
|
||||
};
|
||||
use crate::remote::api::{
|
||||
ClientInfo,
|
||||
};
|
||||
use crate::remote::clients::{
|
||||
self,
|
||||
ClientManager,
|
||||
ClientsEvents,
|
||||
ClientsCommand,
|
||||
ClientsCommands
|
||||
};
|
||||
use crate::remote::builder::{
|
||||
self,
|
||||
IdCertBuilder,
|
||||
SignedMessageBuilder,
|
||||
};
|
||||
use crate::remote::id::{
|
||||
IdCert,
|
||||
MyIdentity,
|
||||
ParentInfo
|
||||
};
|
||||
use crate::remote::responder::{
|
||||
self,
|
||||
Responder,
|
||||
ResponderEvents
|
||||
};
|
||||
use crate::api::publication::{ListReply, PublishDelta, PublishRequest};
|
||||
use crate::api::{ErrorCode, ErrorResponse};
|
||||
use crate::eventsourcing::{Aggregate, AggregateStore, AggregateStoreError, DiskAggregateStore};
|
||||
use crate::remote::api::ClientInfo;
|
||||
use crate::remote::builder::{self, IdCertBuilder, SignedMessageBuilder};
|
||||
use crate::remote::clients::{self, ClientManager, ClientsCommand, ClientsCommands, ClientsEvents};
|
||||
use crate::remote::id::{IdCert, MyIdentity, ParentInfo};
|
||||
use crate::remote::responder::{self, Responder, ResponderEvents};
|
||||
use crate::remote::rfc8181::{
|
||||
self,
|
||||
ErrorReply,
|
||||
Message,
|
||||
ReplyMessage,
|
||||
ReportError,
|
||||
ReportErrorCode,
|
||||
self, ErrorReply, Message, ReplyMessage, ReportError, ReportErrorCode,
|
||||
};
|
||||
use crate::remote::rfc8183::RepositoryResponse;
|
||||
use crate::remote::rfc8183::ServiceUri;
|
||||
use crate::remote::sigmsg::SignedMessage;
|
||||
|
||||
use crate::util::httpclient;
|
||||
use crate::util::softsigner::{OpenSslSigner, SignerError};
|
||||
use rpki::crypto::{PublicKeyFormat, Signer};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProxyServer {
|
||||
signer: OpenSslSigner,
|
||||
clients_store: Arc<DiskAggregateStore<ClientManager>>,
|
||||
responder_store: Arc<DiskAggregateStore<Responder>>,
|
||||
krill_uri: uri::Https
|
||||
krill_uri: uri::Https,
|
||||
}
|
||||
|
||||
/// # Server Life Cycle
|
||||
///
|
||||
impl ProxyServer {
|
||||
|
||||
/// Initialises the Proxy Server. This will re-use the existing clients and
|
||||
/// responder (i.e. server certificate and all), if they exist for this work_dir.
|
||||
/// If they do not exist, they will be initialised as well.
|
||||
@@ -89,18 +47,23 @@ impl ProxyServer {
|
||||
let responder_store = Arc::new(DiskAggregateStore::<Responder>::new(work_dir, "proxy")?);
|
||||
|
||||
let clients_id = clients::id();
|
||||
if ! clients_store.has(&clients_id) {
|
||||
if !clients_store.has(&clients_id) {
|
||||
clients_store.add(ClientsEvents::init())?;
|
||||
}
|
||||
|
||||
let responder_id = responder::id();
|
||||
if ! responder_store.has(&responder_id) {
|
||||
if !responder_store.has(&responder_id) {
|
||||
let my_id = Self::new_id(&mut signer)?;
|
||||
let init = ResponderEvents::init(my_id);
|
||||
responder_store.add(init)?;
|
||||
}
|
||||
|
||||
Ok(ProxyServer { signer, clients_store, responder_store, krill_uri: krill_uri.clone() })
|
||||
Ok(ProxyServer {
|
||||
signer,
|
||||
clients_store,
|
||||
responder_store,
|
||||
krill_uri: krill_uri.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_id(signer: &mut OpenSslSigner) -> Result<MyIdentity, Error> {
|
||||
@@ -139,7 +102,7 @@ impl ProxyServer {
|
||||
handle: &Handle,
|
||||
service_uri: uri::Https,
|
||||
sia_base: uri::Rsync,
|
||||
rrdp_notification_uri: uri::Https
|
||||
rrdp_notification_uri: uri::Https,
|
||||
) -> Result<RepositoryResponse, Error> {
|
||||
let tag = None;
|
||||
|
||||
@@ -157,7 +120,7 @@ impl ProxyServer {
|
||||
id_cert,
|
||||
service_uri,
|
||||
sia_base,
|
||||
rrdp_notification_uri
|
||||
rrdp_notification_uri,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -170,20 +133,21 @@ impl ProxyServer {
|
||||
}
|
||||
|
||||
fn clients(&self) -> Result<Arc<ClientManager>, Error> {
|
||||
self.clients_store.get_latest(&clients::id()).map_err(Error::StoreError)
|
||||
self.clients_store
|
||||
.get_latest(&clients::id())
|
||||
.map_err(Error::StoreError)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Proxy RFC8181 requests to a Krill server
|
||||
///
|
||||
impl ProxyServer {
|
||||
|
||||
/// Takes an RFC8181 request, validates it, and then returns the
|
||||
/// request type
|
||||
pub fn convert_rfc8181_req(
|
||||
&self,
|
||||
msg: SignedMessage,
|
||||
handle: &Handle
|
||||
handle: &Handle,
|
||||
) -> Result<PublishRequest, Error> {
|
||||
self.validate_msg(&msg, handle)?;
|
||||
self.convert_to_json_request(&msg)
|
||||
@@ -208,16 +172,12 @@ impl ProxyServer {
|
||||
self.sign_msg(msg)
|
||||
}
|
||||
|
||||
fn validate_msg(
|
||||
&self,
|
||||
msg: &SignedMessage,
|
||||
handle: &Handle
|
||||
) -> Result<(), Error> {
|
||||
fn validate_msg(&self, msg: &SignedMessage, handle: &Handle) -> Result<(), Error> {
|
||||
match self.clients()?.client_auth(handle) {
|
||||
None => {
|
||||
warn!("Received RFC8181 message for unknown client: {}", &handle);
|
||||
Err(Error::UnknownClient(handle.clone()))
|
||||
},
|
||||
}
|
||||
Some(client) => {
|
||||
let id_cert = client.cert();
|
||||
match msg.validate(id_cert) {
|
||||
@@ -233,10 +193,7 @@ impl ProxyServer {
|
||||
|
||||
/// Retrieves the QueryMessage contained in the SignedMessage and
|
||||
/// converts into the (json) equivalent request for the API.
|
||||
fn convert_to_json_request(
|
||||
&self,
|
||||
msg: &SignedMessage
|
||||
) -> Result<PublishRequest, Error> {
|
||||
fn convert_to_json_request(&self, msg: &SignedMessage) -> Result<PublishRequest, Error> {
|
||||
debug!("Convert contained message to Json equivalent");
|
||||
let msg = rfc8181::Message::from_signed_message(&msg)?;
|
||||
let msg = msg.into_query()?;
|
||||
@@ -246,11 +203,8 @@ impl ProxyServer {
|
||||
fn sign_msg(&self, msg: Message) -> Result<Captured, Error> {
|
||||
let responder = self.responder_store.get_latest(&responder::id())?;
|
||||
|
||||
let builder = SignedMessageBuilder::create(
|
||||
responder.id().key_id(),
|
||||
&self.signer,
|
||||
msg.into_bytes()
|
||||
)?;
|
||||
let builder =
|
||||
SignedMessageBuilder::create(responder.id().key_id(), &self.signer, msg.into_bytes())?;
|
||||
|
||||
let enc = builder.encode();
|
||||
|
||||
@@ -258,8 +212,6 @@ impl ProxyServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -296,35 +248,51 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self { Error::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AggregateStoreError> for Error {
|
||||
fn from(e: AggregateStoreError) -> Self { Error::StoreError(e) }
|
||||
fn from(e: AggregateStoreError) -> Self {
|
||||
Error::StoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<clients::Error> for Error {
|
||||
fn from(e: clients::Error) -> Self { Error::ClientsError(e) }
|
||||
fn from(e: clients::Error) -> Self {
|
||||
Error::ClientsError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignerError> for Error {
|
||||
fn from(e: SignerError) -> Self { Error::SignerError(e) }
|
||||
fn from(e: SignerError) -> Self {
|
||||
Error::SignerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<builder::Error<SignerError>> for Error {
|
||||
fn from(e: builder::Error<SignerError>) -> Self { Error::BuilderError(e) }
|
||||
fn from(e: builder::Error<SignerError>) -> Self {
|
||||
Error::BuilderError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidationError> for Error {
|
||||
fn from(e: ValidationError) -> Self { Error::ValidationError(e) }
|
||||
fn from(e: ValidationError) -> Self {
|
||||
Error::ValidationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rfc8181::MessageError> for Error {
|
||||
fn from(e: rfc8181::MessageError) -> Self { Error::Rfc8181MessageError(e) }
|
||||
fn from(e: rfc8181::MessageError) -> Self {
|
||||
Error::Rfc8181MessageError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<httpclient::Error> for Error {
|
||||
fn from(e: httpclient::Error) -> Self { Error::HttpClientError(e) }
|
||||
fn from(e: httpclient::Error) -> Self {
|
||||
Error::HttpClientError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@@ -333,55 +301,54 @@ impl Error {
|
||||
Error::ValidationError(_) => ReportErrorCode::PermissionFailure,
|
||||
Error::Rfc8181MessageError(_) => ReportErrorCode::XmlError,
|
||||
Error::UnknownClient(_) => ReportErrorCode::PermissionFailure,
|
||||
Error::HttpClientError(http_error) => {
|
||||
match http_error {
|
||||
httpclient::Error::ErrorWithBody(_code, body) => {
|
||||
match serde_json::from_str::<ErrorResponse>(body) {
|
||||
Ok(response) => {
|
||||
let error_nr = response.code();
|
||||
let error_code: ErrorCode = response.into();
|
||||
match error_code {
|
||||
ErrorCode::InvalidPublicationXml => ReportErrorCode::XmlError,
|
||||
ErrorCode::ObjectAlreadyPresent => ReportErrorCode::ObjectAlreadyPresent,
|
||||
ErrorCode::NoObjectForHashAndOrUri => ReportErrorCode::NoObjectMatchingHash,
|
||||
_ => {
|
||||
if error_nr > 2000 && error_nr < 3000 {
|
||||
ReportErrorCode::PermissionFailure
|
||||
} else {
|
||||
ReportErrorCode::OtherError
|
||||
}
|
||||
Error::HttpClientError(http_error) => match http_error {
|
||||
httpclient::Error::ErrorWithBody(_code, body) => {
|
||||
match serde_json::from_str::<ErrorResponse>(body) {
|
||||
Ok(response) => {
|
||||
let error_nr = response.code();
|
||||
let error_code: ErrorCode = response.into();
|
||||
match error_code {
|
||||
ErrorCode::InvalidPublicationXml => ReportErrorCode::XmlError,
|
||||
ErrorCode::ObjectAlreadyPresent => {
|
||||
ReportErrorCode::ObjectAlreadyPresent
|
||||
}
|
||||
ErrorCode::NoObjectForHashAndOrUri => {
|
||||
ReportErrorCode::NoObjectMatchingHash
|
||||
}
|
||||
_ => {
|
||||
if error_nr > 2000 && error_nr < 3000 {
|
||||
ReportErrorCode::PermissionFailure
|
||||
} else {
|
||||
ReportErrorCode::OtherError
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => ReportErrorCode::OtherError
|
||||
}
|
||||
Err(_) => ReportErrorCode::OtherError,
|
||||
}
|
||||
_ => ReportErrorCode::OtherError
|
||||
}
|
||||
|
||||
}
|
||||
_ => ReportErrorCode::OtherError
|
||||
_ => ReportErrorCode::OtherError,
|
||||
},
|
||||
_ => ReportErrorCode::OtherError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// This type proxies native Krill requests to a remote RFC compliant server
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ClientProxy {
|
||||
id: MyIdentity,
|
||||
parent: ParentInfo,
|
||||
work_dir: PathBuf
|
||||
work_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl ClientProxy {
|
||||
pub fn new(
|
||||
id: MyIdentity,
|
||||
parent: ParentInfo,
|
||||
work_dir: PathBuf
|
||||
) -> Self {
|
||||
ClientProxy { id, parent, work_dir }
|
||||
pub fn new(id: MyIdentity, parent: ParentInfo, work_dir: PathBuf) -> Self {
|
||||
ClientProxy {
|
||||
id,
|
||||
parent,
|
||||
work_dir,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Result<ListReply, ClientError> {
|
||||
@@ -391,7 +358,7 @@ impl ClientProxy {
|
||||
match reply {
|
||||
rfc8181::ReplyMessage::ErrorReply(e) => Err(ClientError::ErrorReply(e)),
|
||||
rfc8181::ReplyMessage::SuccessReply => Err(ClientError::UnexpectedReply),
|
||||
rfc8181::ReplyMessage::ListReply(list) => Ok(list)
|
||||
rfc8181::ReplyMessage::ListReply(list) => Ok(list),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,20 +369,16 @@ impl ClientProxy {
|
||||
match reply {
|
||||
rfc8181::ReplyMessage::ErrorReply(e) => Err(ClientError::ErrorReply(e)),
|
||||
rfc8181::ReplyMessage::ListReply(_) => Err(ClientError::UnexpectedReply),
|
||||
rfc8181::ReplyMessage::SuccessReply => Ok(())
|
||||
rfc8181::ReplyMessage::SuccessReply => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn proxy_msg(
|
||||
&self,
|
||||
msg: rfc8181::Message,
|
||||
) -> Result<rfc8181::Message, ClientError> {
|
||||
|
||||
fn proxy_msg(&self, msg: rfc8181::Message) -> Result<rfc8181::Message, ClientError> {
|
||||
let signed = self.sign(msg)?.into_bytes();
|
||||
let res = httpclient::post_binary(
|
||||
&self.parent.service_uri().to_string(),
|
||||
&signed,
|
||||
"application/rpki-publication"
|
||||
"application/rpki-publication",
|
||||
)?;
|
||||
|
||||
let res_msg = SignedMessage::decode(res, true)?;
|
||||
@@ -425,22 +388,16 @@ impl ClientProxy {
|
||||
}
|
||||
|
||||
fn sign(&self, msg: Message) -> Result<Captured, ClientError> {
|
||||
|
||||
let key_id = self.id.key_id();
|
||||
let signer = OpenSslSigner::build(&self.work_dir)?;
|
||||
|
||||
let builder = SignedMessageBuilder::create(
|
||||
key_id,
|
||||
&signer,
|
||||
msg.into_bytes()
|
||||
)?;
|
||||
let builder = SignedMessageBuilder::create(key_id, &signer, msg.into_bytes())?;
|
||||
let enc = builder.encode();
|
||||
|
||||
Ok(enc.to_captured(Mode::Der))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ClientError ----------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -460,41 +417,52 @@ pub enum ClientError {
|
||||
#[display(fmt = "{}", _0)]
|
||||
BuilderError(builder::Error<SignerError>),
|
||||
|
||||
#[display(fmt="Received error from server: {:?}", _0)]
|
||||
#[display(fmt = "Received error from server: {:?}", _0)]
|
||||
ErrorReply(rfc8181::ErrorReply),
|
||||
|
||||
#[display(fmt="Received unexpected reply (list vs success)")]
|
||||
#[display(fmt = "Received unexpected reply (list vs success)")]
|
||||
UnexpectedReply,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
SignerError(SignerError),
|
||||
}
|
||||
|
||||
impl From<httpclient::Error> for ClientError {
|
||||
fn from(e: httpclient::Error) -> Self { ClientError::HttpError(e) }
|
||||
fn from(e: httpclient::Error) -> Self {
|
||||
ClientError::HttpError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bcder::decode::Error> for ClientError {
|
||||
fn from(e: bcder::decode::Error) -> Self { ClientError::DecodeError(e) }
|
||||
fn from(e: bcder::decode::Error) -> Self {
|
||||
ClientError::DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidationError> for ClientError {
|
||||
fn from(e: ValidationError) -> Self { ClientError::ValidationError(e) }
|
||||
fn from(e: ValidationError) -> Self {
|
||||
ClientError::ValidationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rfc8181::MessageError> for ClientError {
|
||||
fn from(e: rfc8181::MessageError) -> Self { ClientError::MessageError(e) }
|
||||
fn from(e: rfc8181::MessageError) -> Self {
|
||||
ClientError::MessageError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<builder::Error<SignerError>> for ClientError {
|
||||
fn from(e: builder::Error<SignerError>) -> Self { ClientError::BuilderError(e) }
|
||||
fn from(e: builder::Error<SignerError>) -> Self {
|
||||
ClientError::BuilderError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignerError> for ClientError {
|
||||
fn from(e: SignerError) -> Self { ClientError::SignerError(e) }
|
||||
fn from(e: SignerError) -> Self {
|
||||
ClientError::SignerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -515,4 +483,4 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use crate::api::admin::Handle;
|
||||
use crate::eventsourcing::{
|
||||
Aggregate,
|
||||
CommandDetails,
|
||||
SentCommand,
|
||||
StoredEvent
|
||||
};
|
||||
use crate::eventsourcing::{Aggregate, CommandDetails, SentCommand, StoredEvent};
|
||||
use crate::remote::id::MyIdentity;
|
||||
|
||||
|
||||
// const fn is not stable yet
|
||||
const ID: &str = "cms-responder";
|
||||
pub fn id() -> Handle { Handle::from(ID) }
|
||||
pub fn id() -> Handle {
|
||||
Handle::from(ID)
|
||||
}
|
||||
|
||||
//------------ ResponderEvent ---------------------------------------------
|
||||
|
||||
@@ -33,7 +29,6 @@ impl ResponderEvents {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ResponderCommand --------------------------------------------
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
@@ -57,7 +52,6 @@ pub struct Responder {
|
||||
id: MyIdentity,
|
||||
}
|
||||
|
||||
|
||||
impl Aggregate for Responder {
|
||||
type Command = ResponderCommand;
|
||||
type Event = ResponderEvent;
|
||||
@@ -67,12 +61,7 @@ impl Aggregate for Responder {
|
||||
fn init(event: Self::InitEvent) -> Result<Self, Self::Error> {
|
||||
let id = event.into_details().id;
|
||||
let version = 1;
|
||||
Ok (
|
||||
Responder {
|
||||
version,
|
||||
id
|
||||
}
|
||||
)
|
||||
Ok(Responder { version, id })
|
||||
}
|
||||
|
||||
fn version(&self) -> u64 {
|
||||
@@ -96,7 +85,6 @@ impl Responder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -107,22 +95,20 @@ pub enum Error {
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::path::PathBuf;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use crate::util::test;
|
||||
use super::*;
|
||||
use crate::eventsourcing::AggregateStore;
|
||||
use crate::eventsourcing::DiskAggregateStore;
|
||||
use crate::util::softsigner::OpenSslSigner;
|
||||
use crate::remote::builder::IdCertBuilder;
|
||||
use super::*;
|
||||
use crate::util::softsigner::OpenSslSigner;
|
||||
use crate::util::test;
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn new_id(work_dir: &PathBuf) -> MyIdentity {
|
||||
let mut s = OpenSslSigner::build(work_dir).unwrap();
|
||||
@@ -135,7 +121,6 @@ mod tests {
|
||||
#[test]
|
||||
fn should_init() {
|
||||
test::test_under_tmp(|d| {
|
||||
|
||||
// Set up a store for the proxy
|
||||
let store = DiskAggregateStore::<Responder>::new(&d, "proxy").unwrap();
|
||||
|
||||
@@ -147,4 +132,4 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+298
-280
File diff suppressed because it is too large
Load Diff
+230
-361
File diff suppressed because it is too large
Load Diff
+161
-204
@@ -3,10 +3,10 @@
|
||||
//! Support for the RFC8183 out-of-band setup requests and responses
|
||||
//! used to exchange identity and configuration between CAs and their
|
||||
//! parent CA and/or RPKI Publication Servers.
|
||||
use std::{io, fmt};
|
||||
use std::path::PathBuf;
|
||||
use std::str::{FromStr, from_utf8_unchecked};
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
use std::str::{from_utf8_unchecked, FromStr};
|
||||
use std::{fmt, io};
|
||||
|
||||
use base64::DecodeError;
|
||||
use bcder::decode;
|
||||
@@ -19,20 +19,13 @@ use rpki::x509::Time;
|
||||
|
||||
use crate::api::admin::Handle;
|
||||
use crate::util::file;
|
||||
use crate::util::xml::{
|
||||
AttributesError,
|
||||
XmlReader,
|
||||
XmlReaderErr,
|
||||
XmlWriter
|
||||
};
|
||||
|
||||
use crate::util::xml::{AttributesError, XmlReader, XmlReaderErr, XmlWriter};
|
||||
|
||||
use crate::remote::id::IdCert;
|
||||
|
||||
pub const VERSION: &str = "1";
|
||||
pub const NS: &str = "http://www.hactrn.net/uris/rpki/rpki-setup/";
|
||||
|
||||
|
||||
//------------ ChildRequest --------------------------------------------------
|
||||
|
||||
/// Type representing a <child_request /> defined in section 5.2.1 of
|
||||
@@ -54,16 +47,26 @@ pub struct ChildRequest {
|
||||
///
|
||||
impl ChildRequest {
|
||||
pub fn new(child_handle: Handle, id_cert: IdCert) -> Self {
|
||||
ChildRequest { tag: None, child_handle, id_cert }
|
||||
ChildRequest {
|
||||
tag: None,
|
||||
child_handle,
|
||||
id_cert,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Option<String>, Handle, IdCert) {
|
||||
(self.tag, self.child_handle, self.id_cert)
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> Option<&String> { self.tag.as_ref() }
|
||||
pub fn child_handle(&self) -> &Handle { &self.child_handle }
|
||||
pub fn id_cert(&self) -> &IdCert { &self.id_cert }
|
||||
pub fn tag(&self) -> Option<&String> {
|
||||
self.tag.as_ref()
|
||||
}
|
||||
pub fn child_handle(&self) -> &Handle {
|
||||
&self.child_handle
|
||||
}
|
||||
pub fn id_cert(&self) -> &IdCert {
|
||||
&self.id_cert
|
||||
}
|
||||
}
|
||||
|
||||
/// # Validation
|
||||
@@ -71,42 +74,49 @@ impl ChildRequest {
|
||||
impl ChildRequest {
|
||||
/// Parses a <child_request /> message, and validates the
|
||||
/// embedded certificate. MUST be a validly signed TA cert.
|
||||
pub fn validate<R>(
|
||||
reader: R
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
pub fn validate<R>(reader: R) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Self::validate_at(reader, Time::now())
|
||||
}
|
||||
|
||||
/// Parses a <child_request /> message.
|
||||
fn validate_at<R>(
|
||||
reader: R,
|
||||
now: Time
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
fn validate_at<R>(reader: R, now: Time) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
XmlReader::decode(reader, |r| {
|
||||
r.take_named_element("child_request", |mut a, r| {
|
||||
if a.take_req("version")? != VERSION {
|
||||
return Err(Error::InvalidVersion)
|
||||
return Err(Error::InvalidVersion);
|
||||
}
|
||||
|
||||
let tag = a.take_opt("tag");
|
||||
let child_handle = Handle::from(a.take_req("child_handle")?);
|
||||
|
||||
if a.take_opt("valid_until").is_some() {
|
||||
warn!("Found deprecated attribute 'valid_until' used by \
|
||||
old rpkid implementations. Ignoring this, but other \
|
||||
things may break.")
|
||||
warn!(
|
||||
"Found deprecated attribute 'valid_until' used by \
|
||||
old rpkid implementations. Ignoring this, but other \
|
||||
things may break."
|
||||
)
|
||||
}
|
||||
|
||||
a.exhausted()?;
|
||||
|
||||
let bytes = r.take_named_element("child_bpki_ta", |a,r| {
|
||||
let bytes = r.take_named_element("child_bpki_ta", |a, r| {
|
||||
a.exhausted()?;
|
||||
r.take_bytes_std()
|
||||
})?;
|
||||
let id_cert = IdCert::decode(bytes)?;
|
||||
id_cert.validate_ta_at(now)?;
|
||||
|
||||
Ok(ChildRequest { child_handle, tag, id_cert })
|
||||
Ok(ChildRequest {
|
||||
child_handle,
|
||||
tag,
|
||||
id_cert,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -118,41 +128,29 @@ impl ChildRequest {
|
||||
/// Encodes the <child_request/> to a Vec
|
||||
pub fn encode_vec(&self) -> Vec<u8> {
|
||||
XmlWriter::encode_vec(|w| {
|
||||
|
||||
let mut a = vec![
|
||||
("xmlns", NS),
|
||||
("version", VERSION),
|
||||
("child_handle", self.child_handle.as_ref())
|
||||
("child_handle", self.child_handle.as_ref()),
|
||||
];
|
||||
|
||||
if let Some(ref t) = self.tag {
|
||||
a.push(("tag", t.as_ref()));
|
||||
}
|
||||
|
||||
w.put_element(
|
||||
"child_request",
|
||||
Some(a.as_ref()),
|
||||
|w| {
|
||||
w.put_element(
|
||||
"child_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
w.put_element("child_request", Some(a.as_ref()), |w| {
|
||||
w.put_element("child_bpki_ta", None, |w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for ChildRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = self.encode_vec();
|
||||
let s = unsafe {
|
||||
from_utf8_unchecked(s.as_slice())
|
||||
};
|
||||
let s = unsafe { from_utf8_unchecked(s.as_slice()) };
|
||||
s.fmt(f)
|
||||
}
|
||||
}
|
||||
@@ -191,35 +189,49 @@ impl ParentResponse {
|
||||
service_uri: ServiceUri,
|
||||
) -> Self {
|
||||
ParentResponse {
|
||||
tag, id_cert, parent_handle, child_handle, service_uri
|
||||
tag,
|
||||
id_cert,
|
||||
parent_handle,
|
||||
child_handle,
|
||||
service_uri,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> Option<&String> { self.tag.as_ref() }
|
||||
pub fn id_cert(&self) -> &IdCert { &self.id_cert }
|
||||
pub fn parent_handle(&self) -> &Handle { &self.parent_handle }
|
||||
pub fn child_handle(&self) -> &Handle { &self.child_handle }
|
||||
pub fn service_uri(&self) -> &ServiceUri { &self.service_uri }
|
||||
pub fn tag(&self) -> Option<&String> {
|
||||
self.tag.as_ref()
|
||||
}
|
||||
pub fn id_cert(&self) -> &IdCert {
|
||||
&self.id_cert
|
||||
}
|
||||
pub fn parent_handle(&self) -> &Handle {
|
||||
&self.parent_handle
|
||||
}
|
||||
pub fn child_handle(&self) -> &Handle {
|
||||
&self.child_handle
|
||||
}
|
||||
pub fn service_uri(&self) -> &ServiceUri {
|
||||
&self.service_uri
|
||||
}
|
||||
}
|
||||
|
||||
/// # Validation
|
||||
///
|
||||
impl ParentResponse {
|
||||
pub fn validate<R>(
|
||||
reader: R
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
pub fn validate<R>(reader: R) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Self::validate_at(reader, Time::now())
|
||||
}
|
||||
|
||||
fn validate_at<R>(
|
||||
reader: R,
|
||||
now: Time
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
fn validate_at<R>(reader: R, now: Time) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
XmlReader::decode(reader, |r| {
|
||||
r.take_named_element("parent_response", |mut a, r| {
|
||||
|
||||
if a.take_req("version")? != VERSION {
|
||||
return Err(Error::InvalidVersion)
|
||||
return Err(Error::InvalidVersion);
|
||||
}
|
||||
|
||||
let tag = a.take_opt("tag");
|
||||
@@ -228,9 +240,11 @@ impl ParentResponse {
|
||||
let service_uri = ServiceUri::try_from(a.take_req("service_uri")?)?;
|
||||
|
||||
if a.take_opt("valid_until").is_some() {
|
||||
warn!("Found deprecated attribute 'valid_until' used by \
|
||||
old rpkid implementations. Ignoring this, but other \
|
||||
things may break.")
|
||||
warn!(
|
||||
"Found deprecated attribute 'valid_until' used by \
|
||||
old rpkid implementations. Ignoring this, but other \
|
||||
things may break."
|
||||
)
|
||||
}
|
||||
|
||||
a.exhausted()?;
|
||||
@@ -248,20 +262,21 @@ impl ParentResponse {
|
||||
///
|
||||
/// I.e. the child needs to set up their publication server
|
||||
/// exchange separately, and explicitly.
|
||||
fn ignore_offer_or_referral<R>(
|
||||
r: &mut XmlReader<R>
|
||||
) -> Result<(), Error> where R: io::Read {
|
||||
fn ignore_offer_or_referral<R>(r: &mut XmlReader<R>) -> Result<(), Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
r.take_opt_element(|tag, _a, r| {
|
||||
match tag.name.as_str() {
|
||||
"offer" => {
|
||||
r.take_empty()?;
|
||||
Ok(None)
|
||||
},
|
||||
}
|
||||
"referral" => {
|
||||
let chars = r.take_chars()?;
|
||||
Ok(Some(chars)) // help return type inference.
|
||||
},
|
||||
_ => Err(Error::InvalidXml)
|
||||
}
|
||||
_ => Err(Error::InvalidXml),
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
@@ -272,7 +287,11 @@ impl ParentResponse {
|
||||
}
|
||||
|
||||
Ok(ParentResponse {
|
||||
tag, id_cert, parent_handle, child_handle, service_uri
|
||||
tag,
|
||||
id_cert,
|
||||
parent_handle,
|
||||
child_handle,
|
||||
service_uri,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -285,7 +304,6 @@ impl ParentResponse {
|
||||
/// Encodes the <parent_response/> to a Vec
|
||||
pub fn encode_vec(&self) -> Vec<u8> {
|
||||
XmlWriter::encode_vec(|w| {
|
||||
|
||||
let service_uri = self.service_uri.to_string();
|
||||
|
||||
let mut a = vec![
|
||||
@@ -300,19 +318,11 @@ impl ParentResponse {
|
||||
a.push(("tag", t.as_ref()));
|
||||
}
|
||||
|
||||
w.put_element(
|
||||
"parent_response",
|
||||
Some(a.as_ref()),
|
||||
|w| {
|
||||
w.put_element(
|
||||
"parent_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
w.put_element("parent_response", Some(a.as_ref()), |w| {
|
||||
w.put_element("parent_bpki_ta", None, |w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -342,9 +352,9 @@ pub struct PublisherRequest {
|
||||
impl PublisherRequest {
|
||||
pub fn new(tag: Option<&str>, publisher_handle: &str, id_cert: IdCert) -> Self {
|
||||
PublisherRequest {
|
||||
tag: tag.map(|s| { s.to_string() }),
|
||||
tag: tag.map(|s| s.to_string()),
|
||||
publisher_handle: publisher_handle.to_string(),
|
||||
id_cert
|
||||
id_cert,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,25 +367,25 @@ impl PublisherRequest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Validation
|
||||
///
|
||||
impl PublisherRequest {
|
||||
pub fn validate<R>(
|
||||
reader: R
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
pub fn validate<R>(reader: R) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Self::validate_at(reader, Time::now())
|
||||
}
|
||||
|
||||
/// Parses a <publisher_request /> message.
|
||||
fn validate_at<R>(
|
||||
reader: R,
|
||||
now: Time
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
fn validate_at<R>(reader: R, now: Time) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
XmlReader::decode(reader, |r| {
|
||||
r.take_named_element("publisher_request", |mut a, r| {
|
||||
if a.take_req("version")? != "1" {
|
||||
return Err(Error::InvalidVersion)
|
||||
return Err(Error::InvalidVersion);
|
||||
}
|
||||
|
||||
let tag = a.take_opt("tag");
|
||||
@@ -390,7 +400,11 @@ impl PublisherRequest {
|
||||
let id_cert = IdCert::decode(bytes)?;
|
||||
id_cert.validate_ta_at(now)?;
|
||||
|
||||
Ok(PublisherRequest { tag, publisher_handle, id_cert })
|
||||
Ok(PublisherRequest {
|
||||
tag,
|
||||
publisher_handle,
|
||||
id_cert,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -402,31 +416,21 @@ impl PublisherRequest {
|
||||
/// Encodes a <publisher_request> to a Vec
|
||||
pub fn encode_vec(&self) -> Vec<u8> {
|
||||
XmlWriter::encode_vec(|w| {
|
||||
|
||||
let mut a = vec![
|
||||
("xmlns", NS),
|
||||
("version", VERSION),
|
||||
("publisher_handle", self.publisher_handle.as_ref())
|
||||
("publisher_handle", self.publisher_handle.as_ref()),
|
||||
];
|
||||
|
||||
if let Some(ref t) = self.tag {
|
||||
a.push(("tag", t.as_ref()));
|
||||
}
|
||||
|
||||
w.put_element(
|
||||
"publisher_request",
|
||||
Some(a.as_ref()),
|
||||
|w| {
|
||||
w.put_element(
|
||||
"publisher_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
w.put_element("publisher_request", Some(a.as_ref()), |w| {
|
||||
w.put_element("publisher_bpki_ta", None, |w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -437,7 +441,6 @@ impl PublisherRequest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RepositoryResponse --------------------------------------------
|
||||
|
||||
/// Type representing a <repository_response/>
|
||||
@@ -465,7 +468,7 @@ pub struct RepositoryResponse {
|
||||
sia_base: uri::Rsync,
|
||||
|
||||
/// The HTTPS notification URI that the CA can use
|
||||
rrdp_notification_uri: uri::Https
|
||||
rrdp_notification_uri: uri::Https,
|
||||
}
|
||||
|
||||
/// # Construct and Data Access
|
||||
@@ -478,7 +481,7 @@ impl RepositoryResponse {
|
||||
id_cert: IdCert,
|
||||
service_uri: ServiceUri,
|
||||
sia_base: uri::Rsync,
|
||||
rrdp_notification_uri: uri::Https
|
||||
rrdp_notification_uri: uri::Https,
|
||||
) -> Self {
|
||||
RepositoryResponse {
|
||||
tag,
|
||||
@@ -486,7 +489,7 @@ impl RepositoryResponse {
|
||||
id_cert,
|
||||
service_uri,
|
||||
sia_base,
|
||||
rrdp_notification_uri
|
||||
rrdp_notification_uri,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,39 +522,36 @@ impl RepositoryResponse {
|
||||
///
|
||||
impl RepositoryResponse {
|
||||
/// Parses a <repository_response /> message.
|
||||
pub fn validate<R>(
|
||||
reader: R
|
||||
) -> Result<Self, Error> where R: io::Read {
|
||||
pub fn validate<R>(reader: R) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Self::validate_at(reader, Time::now())
|
||||
}
|
||||
|
||||
fn validate_at<R>(
|
||||
reader: R,
|
||||
now: Time
|
||||
) -> Result<Self, Error>
|
||||
where R: io::Read {
|
||||
fn validate_at<R>(reader: R, now: Time) -> Result<Self, Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
XmlReader::decode(reader, |r| {
|
||||
r.take_named_element("repository_response", |mut a, r| {
|
||||
if a.take_req("version")? != VERSION {
|
||||
return Err(Error::InvalidVersion)
|
||||
return Err(Error::InvalidVersion);
|
||||
}
|
||||
|
||||
let tag = a.take_opt("tag");
|
||||
let publisher_handle = a.take_req("publisher_handle")?;
|
||||
let service_uri = ServiceUri::try_from(
|
||||
a.take_req("service_uri")?)?;
|
||||
let sia_base = uri::Rsync::from_string(
|
||||
a.take_req("sia_base")?)?;
|
||||
let rrdp_notification_uri = uri::Https::from_string(
|
||||
a.take_req("rrdp_notification_uri")?)?;
|
||||
let service_uri = ServiceUri::try_from(a.take_req("service_uri")?)?;
|
||||
let sia_base = uri::Rsync::from_string(a.take_req("sia_base")?)?;
|
||||
let rrdp_notification_uri =
|
||||
uri::Https::from_string(a.take_req("rrdp_notification_uri")?)?;
|
||||
|
||||
a.exhausted()?;
|
||||
|
||||
let id_cert = r.take_named_element(
|
||||
"repository_bpki_ta", |a, r| {
|
||||
a.exhausted()?;
|
||||
r.take_bytes_std()
|
||||
})?;
|
||||
let id_cert = r.take_named_element("repository_bpki_ta", |a, r| {
|
||||
a.exhausted()?;
|
||||
r.take_bytes_std()
|
||||
})?;
|
||||
|
||||
let id_cert = IdCert::decode(id_cert)?;
|
||||
id_cert.validate_ta_at(now)?;
|
||||
@@ -562,7 +562,7 @@ impl RepositoryResponse {
|
||||
id_cert,
|
||||
service_uri,
|
||||
sia_base,
|
||||
rrdp_notification_uri
|
||||
rrdp_notification_uri,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -575,7 +575,6 @@ impl RepositoryResponse {
|
||||
/// Encodes the <repository_response/> to a Vec
|
||||
pub fn encode_vec(&self) -> Vec<u8> {
|
||||
XmlWriter::encode_vec(|w| {
|
||||
|
||||
let service_uri = self.service_uri.to_string();
|
||||
let sia_base = self.sia_base.to_string();
|
||||
let rrdp_notification_uri = self.rrdp_notification_uri.to_string();
|
||||
@@ -586,27 +585,18 @@ impl RepositoryResponse {
|
||||
("publisher_handle", self.publisher_handle.as_ref()),
|
||||
("service_uri", service_uri.as_ref()),
|
||||
("sia_base", sia_base.as_ref()),
|
||||
("rrdp_notification_uri", rrdp_notification_uri.as_ref())
|
||||
("rrdp_notification_uri", rrdp_notification_uri.as_ref()),
|
||||
];
|
||||
|
||||
if let Some(ref t) = self.tag {
|
||||
a.push(("tag", t.as_ref()));
|
||||
}
|
||||
|
||||
w.put_element(
|
||||
"repository_response",
|
||||
Some(&a),
|
||||
|w| {
|
||||
w.put_element(
|
||||
"repository_bpki_ta",
|
||||
None,
|
||||
|w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
)
|
||||
w.put_element("repository_response", Some(&a), |w| {
|
||||
w.put_element("repository_bpki_ta", None, |w| {
|
||||
w.put_base64_std(&self.id_cert.to_bytes())
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -617,14 +607,13 @@ impl RepositoryResponse {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ ServiceUri ----------------------------------------------------
|
||||
|
||||
/// The service URI where a child or publisher needs to send its
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)]
|
||||
pub enum ServiceUri {
|
||||
Https(uri::Https),
|
||||
Http(String)
|
||||
Http(String),
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ServiceUri {
|
||||
@@ -644,15 +633,13 @@ impl fmt::Display for ServiceUri {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ServiceUri::Http(string) => string.fmt(f),
|
||||
ServiceUri::Https(https) => https.fmt(f)
|
||||
ServiceUri::Https(https) => https.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
#[display(fmt = "Invalid XML")]
|
||||
@@ -720,10 +707,10 @@ impl From<uri::Error> for Error {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rpki::x509::Time;
|
||||
use super::*;
|
||||
use crate::remote::id::tests::test_id_certificate;
|
||||
use crate::util::test;
|
||||
use super::*;
|
||||
use rpki::x509::Time;
|
||||
|
||||
fn rpkid_time() -> Time {
|
||||
Time::utc(2012, 1, 1, 0, 0, 0)
|
||||
@@ -738,18 +725,13 @@ mod tests {
|
||||
}
|
||||
|
||||
fn example_service_uri() -> ServiceUri {
|
||||
ServiceUri::Https(
|
||||
test::https("https://a.example/publication/Alice/Bob-42")
|
||||
)
|
||||
ServiceUri::Https(test::https("https://a.example/publication/Alice/Bob-42"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_rpkid_publisher_request() {
|
||||
let xml = include_str!("../../test-resources/oob/publisher_request.xml");
|
||||
let pr = PublisherRequest::validate_at(
|
||||
xml.as_bytes(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let pr = PublisherRequest::validate_at(xml.as_bytes(), rpkid_time()).unwrap();
|
||||
assert_eq!("Bob".to_string(), pr.publisher_handle);
|
||||
assert_eq!(Some("A0001".to_string()), pr.tag);
|
||||
}
|
||||
@@ -757,10 +739,7 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_rpkid_repository_response() {
|
||||
let xml = include_str!("../../test-resources/oob/repository_response.xml");
|
||||
let rr = RepositoryResponse::validate_at(
|
||||
xml.as_bytes(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let rr = RepositoryResponse::validate_at(xml.as_bytes(), rpkid_time()).unwrap();
|
||||
assert_eq!(Some("A0001".to_string()), rr.tag);
|
||||
assert_eq!("Alice/Bob-42".to_string(), rr.publisher_handle);
|
||||
assert_eq!(example_service_uri(), rr.service_uri);
|
||||
@@ -775,15 +754,12 @@ mod tests {
|
||||
let pr = PublisherRequest {
|
||||
tag: Some("tag".to_string()),
|
||||
publisher_handle: "tim".to_string(),
|
||||
id_cert: cert
|
||||
id_cert: cert,
|
||||
};
|
||||
|
||||
let enc = pr.encode_vec();
|
||||
|
||||
PublisherRequest::validate_at(
|
||||
enc.as_slice(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
PublisherRequest::validate_at(enc.as_slice(), rpkid_time()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -796,33 +772,24 @@ mod tests {
|
||||
rrdp_notification_uri: example_rrdp_uri(),
|
||||
sia_base: example_sia_base(),
|
||||
service_uri: example_service_uri(),
|
||||
id_cert: cert
|
||||
id_cert: cert,
|
||||
};
|
||||
|
||||
let enc = pr.encode_vec();
|
||||
|
||||
RepositoryResponse::validate_at(
|
||||
enc.as_slice(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
RepositoryResponse::validate_at(enc.as_slice(), rpkid_time()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_request() {
|
||||
let xml = include_str!("../../test-resources/remote/rpkid-child-id.xml");
|
||||
let req = ChildRequest::validate_at(
|
||||
xml.as_bytes(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let req = ChildRequest::validate_at(xml.as_bytes(), rpkid_time()).unwrap();
|
||||
|
||||
assert_eq!(&Handle::from("Carol"), req.child_handle());
|
||||
assert_eq!(None, req.tag());
|
||||
|
||||
let encoded = req.encode_vec();
|
||||
let decoded = ChildRequest::validate_at(
|
||||
encoded.as_slice(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let decoded = ChildRequest::validate_at(encoded.as_slice(), rpkid_time()).unwrap();
|
||||
|
||||
assert_eq!(req, decoded);
|
||||
}
|
||||
@@ -830,27 +797,17 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_rpkid_parent_response_referral() {
|
||||
let xml = include_str!("../../test-resources/remote/rpkid-parent-response-referral.xml");
|
||||
let _res = ParentResponse::validate_at(
|
||||
xml.as_bytes(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let _res = ParentResponse::validate_at(xml.as_bytes(), rpkid_time()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent_response() {
|
||||
let xml = include_str!("../../test-resources/remote/rpkid-parent-response-offer.xml");
|
||||
let res = ParentResponse::validate_at(
|
||||
xml.as_bytes(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let res = ParentResponse::validate_at(xml.as_bytes(), rpkid_time()).unwrap();
|
||||
|
||||
let encoded = res.encode_vec();
|
||||
let decoded = ParentResponse::validate_at(
|
||||
encoded.as_slice(),
|
||||
rpkid_time()
|
||||
).unwrap();
|
||||
let decoded = ParentResponse::validate_at(encoded.as_slice(), rpkid_time()).unwrap();
|
||||
|
||||
assert_eq!(res, decoded);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,12 @@
|
||||
use bytes::Bytes;
|
||||
|
||||
use bcder::decode;
|
||||
use bcder::{Mode, Oid, Tag};
|
||||
use bcder::string::OctetString;
|
||||
use bcder::{Mode, Oid, Tag};
|
||||
|
||||
use rpki::crypto::{
|
||||
DigestAlgorithm,
|
||||
KeyIdentifier,
|
||||
Signature,
|
||||
SignatureAlgorithm
|
||||
};
|
||||
use rpki::crypto::{DigestAlgorithm, KeyIdentifier, Signature, SignatureAlgorithm};
|
||||
use rpki::oid;
|
||||
use rpki::sigobj::{
|
||||
SignedAttrs,
|
||||
MessageDigest
|
||||
};
|
||||
use rpki::sigobj::{MessageDigest, SignedAttrs};
|
||||
use rpki::x509::{Time, ValidationError};
|
||||
|
||||
use crate::remote::id::IdCert;
|
||||
@@ -46,22 +38,15 @@ pub struct SignedMessage {
|
||||
|
||||
//--- SignedAttributes
|
||||
//
|
||||
message_digest: MessageDigest
|
||||
message_digest: MessageDigest,
|
||||
}
|
||||
|
||||
/// # Decoding
|
||||
///
|
||||
impl SignedMessage {
|
||||
|
||||
pub fn decode<S: decode::Source>(
|
||||
source: S,
|
||||
strict: bool
|
||||
) -> Result<Self, S::Err> {
|
||||
if strict { Mode::Der }
|
||||
else { Mode::Ber }
|
||||
.decode(source, Self::take_from)
|
||||
pub fn decode<S: decode::Source>(source: S, strict: bool) -> Result<Self, S::Err> {
|
||||
if strict { Mode::Der } else { Mode::Ber }.decode(source, Self::take_from)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// # Accessors
|
||||
@@ -75,56 +60,50 @@ impl SignedMessage {
|
||||
/// # Parsing
|
||||
///
|
||||
impl SignedMessage {
|
||||
|
||||
fn take_signed_data<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
cons: &mut decode::Constructed<S>,
|
||||
) -> Result<Self, S::Err> {
|
||||
cons.take_sequence(|cons| {
|
||||
cons.skip_u8_if(3)?; // version -- must be 3
|
||||
|
||||
let digest_algorithm =
|
||||
DigestAlgorithm::take_set_from(cons)?;
|
||||
let digest_algorithm = DigestAlgorithm::take_set_from(cons)?;
|
||||
|
||||
let (content_type, content) = {
|
||||
cons.take_sequence(|cons| { // encapContentInfo
|
||||
cons.take_sequence(|cons| {
|
||||
// encapContentInfo
|
||||
Ok((
|
||||
Oid::take_from(cons)?,
|
||||
cons.take_constructed_if(
|
||||
Tag::CTX_0,
|
||||
OctetString::take_from
|
||||
)?
|
||||
cons.take_constructed_if(Tag::CTX_0, OctetString::take_from)?,
|
||||
))
|
||||
})?
|
||||
};
|
||||
if content_type != oid::PROTOCOL_CONTENT_TYPE {
|
||||
return xerr!(Err(decode::Malformed.into()))
|
||||
return xerr!(Err(decode::Malformed.into()));
|
||||
}
|
||||
|
||||
let id_cert = Self::take_certificates(cons)?;
|
||||
|
||||
let _whatever = Self::drop_crls(cons);
|
||||
|
||||
|
||||
let (sid, attrs, signature) = { // signerInfos
|
||||
let (sid, attrs, signature) = {
|
||||
// signerInfos
|
||||
cons.take_set(|cons| {
|
||||
cons.take_sequence(|cons| {
|
||||
cons.skip_u8_if(3)?;
|
||||
let sid = cons.take_value_if(
|
||||
Tag::CTX_0, |content| {
|
||||
KeyIdentifier::from_content(content)
|
||||
}
|
||||
)?;
|
||||
let sid = cons.take_value_if(Tag::CTX_0, |content| {
|
||||
KeyIdentifier::from_content(content)
|
||||
})?;
|
||||
let alg = DigestAlgorithm::take_from(cons)?;
|
||||
if alg != digest_algorithm {
|
||||
return Err(decode::Malformed.into())
|
||||
return Err(decode::Malformed.into());
|
||||
}
|
||||
let attrs = SignedAttrs::take_from_signed_message(cons)?;
|
||||
if attrs.2 != content_type {
|
||||
return Err(decode::Malformed.into())
|
||||
return Err(decode::Malformed.into());
|
||||
}
|
||||
let signature = Signature::new(
|
||||
SignatureAlgorithm::cms_take_from(cons)?,
|
||||
OctetString::take_from(cons)?.into_bytes()
|
||||
OctetString::take_from(cons)?.into_bytes(),
|
||||
);
|
||||
// no unsignedAttributes
|
||||
Ok((sid, attrs, signature))
|
||||
@@ -142,15 +121,12 @@ impl SignedMessage {
|
||||
signed_attrs: attrs.0,
|
||||
signature,
|
||||
|
||||
message_digest: attrs.1
|
||||
message_digest: attrs.1,
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
pub fn take_from<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
) -> Result<Self, S::Err> {
|
||||
pub fn take_from<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<Self, S::Err> {
|
||||
cons.take_sequence(|cons| {
|
||||
oid::SIGNED_DATA.skip_if(cons)?; // contentType
|
||||
cons.take_constructed_if(Tag::CTX_0, Self::take_signed_data)
|
||||
@@ -158,16 +134,12 @@ impl SignedMessage {
|
||||
}
|
||||
|
||||
fn take_certificates<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
cons: &mut decode::Constructed<S>,
|
||||
) -> Result<IdCert, S::Err> {
|
||||
cons.take_constructed_if(Tag::CTX_0, |cons| {
|
||||
cons.take_constructed(|tag, cons| {
|
||||
match tag {
|
||||
Tag::SEQUENCE => IdCert::from_constructed(cons),
|
||||
_ => {
|
||||
xerr!(Err(decode::Unimplemented.into()))
|
||||
}
|
||||
}
|
||||
cons.take_constructed(|tag, cons| match tag {
|
||||
Tag::SEQUENCE => IdCert::from_constructed(cons),
|
||||
_ => xerr!(Err(decode::Unimplemented.into())),
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -182,16 +154,11 @@ impl SignedMessage {
|
||||
// re-signed CRL by the CA cert for inclusion.. then if the EE
|
||||
// key is stolen you get a bit of protection.
|
||||
//
|
||||
fn drop_crls<S: decode::Source>(
|
||||
cons: &mut decode::Constructed<S>
|
||||
) -> Result<(), S::Err> {
|
||||
cons.take_constructed_if(Tag::CTX_1, |cons| {
|
||||
cons.skip_all()
|
||||
})
|
||||
fn drop_crls<S: decode::Source>(cons: &mut decode::Constructed<S>) -> Result<(), S::Err> {
|
||||
cons.take_constructed_if(Tag::CTX_1, |cons| cons.skip_all())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Validation
|
||||
///
|
||||
impl SignedMessage {
|
||||
@@ -204,11 +171,7 @@ impl SignedMessage {
|
||||
}
|
||||
|
||||
/// Validates a signed message for a given point in time.
|
||||
pub fn validate_at(
|
||||
&self,
|
||||
issuer: &IdCert,
|
||||
now: Time
|
||||
) -> Result<(), ValidationError> {
|
||||
pub fn validate_at(&self, issuer: &IdCert, now: Time) -> Result<(), ValidationError> {
|
||||
self.id_cert.validate_ee_at(issuer, now)?;
|
||||
self.verify_signature()?;
|
||||
Ok(())
|
||||
@@ -224,17 +187,16 @@ impl SignedMessage {
|
||||
context.finish()
|
||||
};
|
||||
if digest.as_ref() != self.message_digest.as_ref() {
|
||||
return Err(ValidationError)
|
||||
return Err(ValidationError);
|
||||
}
|
||||
let msg = self.signed_attrs.encode_verify();
|
||||
self.id_cert.subject_public_key_info().verify(
|
||||
&msg,
|
||||
&self.signature
|
||||
).map_err(Into::into)
|
||||
self.id_cert
|
||||
.subject_public_key_info()
|
||||
.verify(&msg, &self.signature)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -244,30 +206,26 @@ mod tests {
|
||||
#[test]
|
||||
fn should_parse_and_validate_signed_message() {
|
||||
let der = include_bytes!("../../test-resources/remote/pdu_200.der");
|
||||
let msg = SignedMessage::decode(
|
||||
Bytes::from_static(der), false
|
||||
).unwrap();
|
||||
let msg = SignedMessage::decode(Bytes::from_static(der), false).unwrap();
|
||||
|
||||
let b = include_bytes!("../../test-resources/remote/cms_ta.cer");
|
||||
let id_cert = IdCert::decode(Bytes::from_static(b)).unwrap();
|
||||
|
||||
msg.validate_at(&id_cert, Time::utc(2012, 1, 1, 0, 0, 0)).unwrap();
|
||||
msg.validate_at(&id_cert, Time::utc(2012, 1, 1, 0, 0, 0))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reject_invalid_signed_message() {
|
||||
let der = include_bytes!("../../test-resources/remote/pdu_200.der");
|
||||
let msg = SignedMessage::decode(
|
||||
Bytes::from_static(der), false
|
||||
).unwrap();
|
||||
let msg = SignedMessage::decode(Bytes::from_static(der), false).unwrap();
|
||||
|
||||
let b = include_bytes!("../../test-resources/oob/id_publisher_ta.cer");
|
||||
let id_cert = IdCert::decode(Bytes::from_static(b)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
msg.validate_at(
|
||||
&id_cert, Time::utc(2012, 1, 1, 0, 0, 0)
|
||||
).unwrap_err(),
|
||||
msg.validate_at(&id_cert, Time::utc(2012, 1, 1, 0, 0, 0))
|
||||
.unwrap_err(),
|
||||
ValidationError,
|
||||
);
|
||||
}
|
||||
@@ -275,9 +233,6 @@ mod tests {
|
||||
#[test]
|
||||
fn parse_lacnic_issue_response() {
|
||||
let der = include_bytes!("../../test-resources/remote/lacnic-res-2.der");
|
||||
let _msg = SignedMessage::decode(
|
||||
Bytes::from_static(der),
|
||||
false
|
||||
).unwrap();
|
||||
let _msg = SignedMessage::decode(Bytes::from_static(der), false).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
use base64;
|
||||
use bytes::Bytes;
|
||||
use log::LevelFilter;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::de;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use syslog::Facility;
|
||||
|
||||
|
||||
//------------ Bytes ---------------------------------------------------------
|
||||
|
||||
pub fn de_bytes<'de, D>(d: D) -> Result<Bytes, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let some = String::deserialize(d)?;
|
||||
let dec = base64::decode(&some).map_err(de::Error::custom)?;
|
||||
@@ -18,31 +18,31 @@ where D: Deserializer<'de>
|
||||
}
|
||||
|
||||
pub fn ser_bytes<S>(b: &Bytes, s: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
base64::encode(b).serialize(s)
|
||||
}
|
||||
|
||||
|
||||
//------------ LevelFilter ---------------------------------------------------
|
||||
|
||||
pub fn de_level_filter<'de, D>(d: D) -> Result<LevelFilter, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use std::str::FromStr;
|
||||
let string = String::deserialize(d)?;
|
||||
LevelFilter::from_str(&string).map_err(de::Error::custom)
|
||||
}
|
||||
|
||||
|
||||
//------------ Facility ------------------------------------------------------
|
||||
|
||||
pub fn de_facility<'de, D>(d: D) -> Result<Facility, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use std::str::FromStr;
|
||||
let string = String::deserialize(d)?;
|
||||
Facility::from_str(&string).map_err(
|
||||
|_| { de::Error::custom(
|
||||
format!("Unsupported syslog_facility: \"{}\"", string))})
|
||||
}
|
||||
Facility::from_str(&string)
|
||||
.map_err(|_| de::Error::custom(format!("Unsupported syslog_facility: \"{}\"", string)))
|
||||
}
|
||||
|
||||
+32
-61
@@ -1,15 +1,14 @@
|
||||
use crate::api::publication;
|
||||
use crate::api::{Base64, EncodedHash};
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
use crate::api::{ Base64, EncodedHash };
|
||||
use crate::api::publication;
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
|
||||
/// Creates a sub dir if needed, return full path to it
|
||||
pub fn sub_dir(base: &PathBuf, name: &str) -> Result<PathBuf, io::Error> {
|
||||
@@ -20,14 +19,14 @@ pub fn sub_dir(base: &PathBuf, name: &str) -> Result<PathBuf, io::Error> {
|
||||
}
|
||||
|
||||
pub fn create_dir(dir: &PathBuf) -> Result<(), io::Error> {
|
||||
if ! dir.is_dir() {
|
||||
if !dir.is_dir() {
|
||||
fs::create_dir(dir)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_file_with_path(path: &PathBuf) -> Result<File, io::Error> {
|
||||
if ! path.exists() {
|
||||
if !path.exists() {
|
||||
if let Some(parent) = path.parent() {
|
||||
trace!("Creating path: {}", parent.to_string_lossy());
|
||||
fs::create_dir_all(parent)?;
|
||||
@@ -43,7 +42,6 @@ pub fn file_path(base_path: &PathBuf, file_name: &str) -> PathBuf {
|
||||
path
|
||||
}
|
||||
|
||||
|
||||
/// Saves a file, creating parent dirs as needed
|
||||
pub fn save(content: &Bytes, full_path: &PathBuf) -> Result<(), io::Error> {
|
||||
let mut f = create_file_with_path(full_path)?;
|
||||
@@ -64,15 +62,11 @@ pub fn save_json<O: Serialize>(object: &O, full_path: &PathBuf) -> Result<(), io
|
||||
pub fn load_json<O: DeserializeOwned>(full_path: &PathBuf) -> Result<O, io::Error> {
|
||||
let bytes = read(full_path)?;
|
||||
serde_json::from_slice(&bytes)
|
||||
.map_err(|_|
|
||||
io::Error::new(io::ErrorKind::Other, "could not deserialize json"))
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "could not deserialize json"))
|
||||
}
|
||||
|
||||
/// Saves a file, creating parent dirs as needed
|
||||
pub fn save_in_dir(
|
||||
content: &Bytes,
|
||||
base_path: &PathBuf,
|
||||
name: &str) -> Result<(), io::Error> {
|
||||
pub fn save_in_dir(content: &Bytes, base_path: &PathBuf, name: &str) -> Result<(), io::Error> {
|
||||
let mut full_path = base_path.clone();
|
||||
full_path.push(name);
|
||||
save(content, &full_path)
|
||||
@@ -83,7 +77,7 @@ pub fn save_in_dir(
|
||||
pub fn save_with_rsync_uri(
|
||||
content: &Bytes,
|
||||
base_path: &PathBuf,
|
||||
uri: &uri::Rsync
|
||||
uri: &uri::Rsync,
|
||||
) -> Result<(), io::Error> {
|
||||
let path = path_with_rsync(base_path, uri);
|
||||
save(content, &path)
|
||||
@@ -97,25 +91,16 @@ pub fn read(path: &PathBuf) -> Result<Bytes, io::Error> {
|
||||
Ok(Bytes::from(bytes))
|
||||
}
|
||||
|
||||
pub fn read_with_rsync_uri(
|
||||
base_path: &PathBuf,
|
||||
uri: &uri::Rsync
|
||||
) -> Result<Bytes, io::Error> {
|
||||
pub fn read_with_rsync_uri(base_path: &PathBuf, uri: &uri::Rsync) -> Result<Bytes, io::Error> {
|
||||
let path = path_with_rsync(base_path, uri);
|
||||
read(&path)
|
||||
}
|
||||
|
||||
pub fn delete_with_rsync_uri(
|
||||
base_path: &PathBuf,
|
||||
uri: &uri::Rsync
|
||||
) -> Result<(), io::Error> {
|
||||
pub fn delete_with_rsync_uri(base_path: &PathBuf, uri: &uri::Rsync) -> Result<(), io::Error> {
|
||||
delete(&path_with_rsync(base_path, uri))
|
||||
}
|
||||
|
||||
pub fn delete_in_dir(
|
||||
base_path: &PathBuf,
|
||||
name: &str
|
||||
) -> Result<(), io::Error> {
|
||||
pub fn delete_in_dir(base_path: &PathBuf, name: &str) -> Result<(), io::Error> {
|
||||
let mut full_path = base_path.clone();
|
||||
full_path.push(name);
|
||||
delete(&full_path)
|
||||
@@ -154,14 +139,13 @@ fn path_with_rsync(base_path: &PathBuf, uri: &uri::Rsync) -> PathBuf {
|
||||
path
|
||||
}
|
||||
|
||||
|
||||
/// Recurses a path on disk and returns all files found as ['CurrentFile'],
|
||||
/// using the provided rsync_base URI as the rsync prefix.
|
||||
/// Allows a publication client to publish the contents below some base
|
||||
/// dir, in their own designated rsync URI name space.
|
||||
pub fn crawl_incl_rsync_base(
|
||||
base_path: &PathBuf,
|
||||
rsync_base: &uri::Rsync
|
||||
rsync_base: &uri::Rsync,
|
||||
) -> Result<Vec<CurrentFile>, Error> {
|
||||
crawl_disk(base_path, base_path, Some(rsync_base))
|
||||
}
|
||||
@@ -170,16 +154,14 @@ pub fn crawl_incl_rsync_base(
|
||||
/// deriving the rsync_base URI from the directory structure. This is
|
||||
/// useful when reading ['CurrentFile'] instances that were saved in some
|
||||
/// base directory as is done by the ['FileStore'].
|
||||
pub fn crawl_derive_rsync_uri(
|
||||
base_path: &PathBuf
|
||||
) -> Result<Vec<CurrentFile>, Error> {
|
||||
pub fn crawl_derive_rsync_uri(base_path: &PathBuf) -> Result<Vec<CurrentFile>, Error> {
|
||||
crawl_disk(base_path, base_path, None)
|
||||
}
|
||||
|
||||
fn crawl_disk(
|
||||
base_path: &PathBuf,
|
||||
path: &PathBuf,
|
||||
rsync_base: Option<&uri::Rsync>
|
||||
rsync_base: Option<&uri::Rsync>,
|
||||
) -> Result<Vec<CurrentFile>, Error> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
@@ -204,7 +186,7 @@ fn crawl_disk(
|
||||
fn derive_uri(
|
||||
base_path: &PathBuf,
|
||||
path: &PathBuf,
|
||||
rsync_base: Option<&uri::Rsync>
|
||||
rsync_base: Option<&uri::Rsync>,
|
||||
) -> Result<uri::Rsync, Error> {
|
||||
let rel = path
|
||||
.strip_prefix(base_path)
|
||||
@@ -213,25 +195,21 @@ fn derive_uri(
|
||||
let rel_string = rel.to_string_lossy().to_string();
|
||||
|
||||
let uri_string = match rsync_base {
|
||||
Some(rsync_base) =>
|
||||
format!("{}{}", rsync_base.to_string(), rel_string),
|
||||
None =>
|
||||
format!("rsync://{}", rel_string)
|
||||
Some(rsync_base) => format!("{}{}", rsync_base.to_string(), rel_string),
|
||||
None => format!("rsync://{}", rel_string),
|
||||
};
|
||||
|
||||
let uri = uri::Rsync::from_str(&uri_string)
|
||||
.map_err(|_| Error::UnsupportedFileName(uri_string))?;
|
||||
let uri =
|
||||
uri::Rsync::from_str(&uri_string).map_err(|_| Error::UnsupportedFileName(uri_string))?;
|
||||
Ok(uri)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ CurrentFile ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CurrentFile {
|
||||
/// The full uri for this file.
|
||||
uri: uri::Rsync,
|
||||
uri: uri::Rsync,
|
||||
|
||||
/// The actual file content. Note that we may want to store this
|
||||
/// only on disk in future (look up by sha256 hash), to save memory.
|
||||
@@ -241,15 +219,14 @@ pub struct CurrentFile {
|
||||
/// in the publication protocol for list, update and withdraw). Saving
|
||||
/// this rather than calculating on demand seems a small price for some
|
||||
/// performance gain.
|
||||
hash: EncodedHash
|
||||
hash: EncodedHash,
|
||||
}
|
||||
|
||||
|
||||
impl CurrentFile {
|
||||
pub fn new(uri: uri::Rsync, content: &Bytes) -> Self {
|
||||
let content = Base64::from_content(&content);
|
||||
let hash = content.to_encoded_hash();
|
||||
CurrentFile {uri, content, hash}
|
||||
CurrentFile { uri, content, hash }
|
||||
}
|
||||
|
||||
/// Saves this file under a base directory, based on the (rsync) uri of
|
||||
@@ -299,28 +276,23 @@ impl CurrentFile {
|
||||
pub fn into_list_element(self) -> publication::ListElement {
|
||||
publication::ListElement::new(self.uri, self.hash)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl PartialEq for CurrentFile {
|
||||
fn eq(&self, other: &CurrentFile) -> bool {
|
||||
self.uri == other.uri &&
|
||||
self.hash == other.hash &&
|
||||
self.content == other.content
|
||||
self.uri == other.uri && self.hash == other.hash && self.content == other.content
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for CurrentFile {}
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
|
||||
#[display(fmt="Cannot read: {}", _0)]
|
||||
#[display(fmt = "Cannot read: {}", _0)]
|
||||
CannotRead(String),
|
||||
|
||||
#[display(fmt="Unsupported characters: {}", _0)]
|
||||
#[display(fmt = "Unsupported characters: {}", _0)]
|
||||
UnsupportedFileName(String),
|
||||
|
||||
#[display(fmt = "Cannot use path outside of rsync jail")]
|
||||
@@ -352,22 +324,21 @@ mod tests {
|
||||
#[test]
|
||||
fn should_scan_disk() {
|
||||
test::test_under_tmp(|base_dir| {
|
||||
|
||||
let file_1 = CurrentFile::new(
|
||||
test::rsync("rsync://host:10873/module/alice/file1.txt"),
|
||||
&Bytes::from("content 1")
|
||||
&Bytes::from("content 1"),
|
||||
);
|
||||
let file_2 = CurrentFile::new(
|
||||
test::rsync("rsync://host:10873/module/alice/file2.txt"),
|
||||
&Bytes::from("content 2")
|
||||
&Bytes::from("content 2"),
|
||||
);
|
||||
let file_3 = CurrentFile::new(
|
||||
test::rsync("rsync://host:10873/module/alice/sub/file1.txt"),
|
||||
&Bytes::from("content sub file")
|
||||
&Bytes::from("content sub file"),
|
||||
);
|
||||
let file_4 = CurrentFile::new(
|
||||
test::rsync("rsync://host:10873/module/bob/file.txt"),
|
||||
&Bytes::from("content")
|
||||
&Bytes::from("content"),
|
||||
);
|
||||
|
||||
file_1.save(&base_dir).unwrap();
|
||||
|
||||
+71
-105
@@ -1,29 +1,21 @@
|
||||
//! Some helper functions for HTTP calls
|
||||
use bytes::Bytes;
|
||||
use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue, CONTENT_TYPE, USER_AGENT};
|
||||
use reqwest::{Client, Response, StatusCode};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::io::Read;
|
||||
use std::time::Duration;
|
||||
use bytes::Bytes;
|
||||
use reqwest::{Client, Response, StatusCode};
|
||||
use reqwest::header::{
|
||||
HeaderMap,
|
||||
HeaderValue,
|
||||
InvalidHeaderValue,
|
||||
USER_AGENT,
|
||||
CONTENT_TYPE};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::api::ErrorResponse;
|
||||
use crate::api::admin::Token;
|
||||
use crate::api::ErrorResponse;
|
||||
|
||||
const JSON_CONTENT: &str = "application/json";
|
||||
|
||||
/// Performs a GET request that expects a json response that can be
|
||||
/// deserialized into the an owned value of the expected type. Returns an error
|
||||
/// if nothing is returned.
|
||||
pub fn get_json<T: DeserializeOwned>(
|
||||
uri: &str,
|
||||
token: Option<&Token>
|
||||
) -> Result<T, Error> {
|
||||
pub fn get_json<T: DeserializeOwned>(uri: &str, token: Option<&Token>) -> Result<T, Error> {
|
||||
let headers = headers(Some(JSON_CONTENT), token)?;
|
||||
let res = client(uri)?.get(uri).headers(headers).send()?;
|
||||
process_json_response(res)
|
||||
@@ -31,16 +23,12 @@ pub fn get_json<T: DeserializeOwned>(
|
||||
|
||||
/// Performs a get request and expects a response that can be turned
|
||||
/// into a string (in particular, not a binary response).
|
||||
pub fn get_text(
|
||||
uri: &str,
|
||||
content_type: &str,
|
||||
token: Option<&Token>
|
||||
) -> Result<String, Error> {
|
||||
pub fn get_text(uri: &str, content_type: &str, token: Option<&Token>) -> Result<String, Error> {
|
||||
let headers = headers(Some(content_type), token)?;
|
||||
let res = client(uri)?.get(uri).headers(headers).send()?;
|
||||
match opt_text_response(res)? {
|
||||
Some(res) => Ok(res),
|
||||
None => Err(Error::EmptyResponse)
|
||||
None => Err(Error::EmptyResponse),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,14 +41,9 @@ pub fn get_ok(uri: &str, token: Option<&Token>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Performs a POST of data that can be serialized into json, and expects
|
||||
/// a 200 OK response, without a body.
|
||||
pub fn post_json(
|
||||
uri: &str,
|
||||
data: impl Serialize,
|
||||
token: Option<&Token>
|
||||
) -> Result<(), Error> {
|
||||
pub fn post_json(uri: &str, data: impl Serialize, token: Option<&Token>) -> Result<(), Error> {
|
||||
let headers = headers(Some(JSON_CONTENT), token)?;
|
||||
let body = serde_json::to_string(&data)?;
|
||||
let res = client(uri)?.post(uri).headers(headers).body(body).send()?;
|
||||
@@ -71,14 +54,13 @@ pub fn post_json(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Performs a POST of data that can be serialized into json, and expects
|
||||
/// a json response that can be deserialized into the an owned value of the
|
||||
/// expected type.
|
||||
pub fn post_json_with_response<T: DeserializeOwned>(
|
||||
uri: &str,
|
||||
data: impl Serialize,
|
||||
token: Option<&Token>
|
||||
token: Option<&Token>,
|
||||
) -> Result<T, Error> {
|
||||
let headers = headers(Some(JSON_CONTENT), token)?;
|
||||
let body = serde_json::to_string(&data)?;
|
||||
@@ -97,16 +79,11 @@ pub fn post_empty(uri: &str, token: Option<&Token>) -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Posts binary data, and expects a binary response.
|
||||
///
|
||||
/// Note: Bytes may be empty if the post was successful, but the response was
|
||||
/// empty.
|
||||
pub fn post_binary(
|
||||
uri: &str,
|
||||
data: &Bytes,
|
||||
content_type: &str
|
||||
) -> Result<Bytes, Error> {
|
||||
pub fn post_binary(uri: &str, data: &Bytes, content_type: &str) -> Result<Bytes, Error> {
|
||||
let headers = headers(Some(content_type), None)?;
|
||||
let body = data.to_vec();
|
||||
|
||||
@@ -118,71 +95,58 @@ pub fn post_binary(
|
||||
res.read_to_end(&mut bytes).unwrap();
|
||||
let bytes = bytes::Bytes::from(bytes);
|
||||
Ok(bytes)
|
||||
},
|
||||
status => {
|
||||
match res.text() {
|
||||
Ok(body) => {
|
||||
if body.is_empty() {
|
||||
Err(Error::BadStatus(status))
|
||||
} else {
|
||||
Err(Error::ErrorWithBody(status, body))
|
||||
}
|
||||
},
|
||||
_ => Err(Error::BadStatus(status))
|
||||
}
|
||||
}
|
||||
status => match res.text() {
|
||||
Ok(body) => {
|
||||
if body.is_empty() {
|
||||
Err(Error::BadStatus(status))
|
||||
} else {
|
||||
Err(Error::ErrorWithBody(status, body))
|
||||
}
|
||||
}
|
||||
_ => Err(Error::BadStatus(status)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a delete request to the specified url.
|
||||
pub fn delete(
|
||||
uri: &str,
|
||||
token: Option<&Token>
|
||||
) -> Result<(), Error> {
|
||||
pub fn delete(uri: &str, token: Option<&Token>) -> Result<(), Error> {
|
||||
let headers = headers(None, token)?;
|
||||
client(uri)?.delete(uri).headers(headers).send()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn client(uri: &str) -> Result<Client, Error> {
|
||||
let builder = Client::builder().gzip(true).timeout(Duration::from_secs(300));
|
||||
let builder = Client::builder()
|
||||
.gzip(true)
|
||||
.timeout(Duration::from_secs(300));
|
||||
|
||||
if uri.starts_with("https://localhost") || uri.starts_with("https://127.0.0.1") {
|
||||
builder.danger_accept_invalid_certs(true)
|
||||
.build().map_err(Error::RequestError)
|
||||
builder
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.map_err(Error::RequestError)
|
||||
} else {
|
||||
builder.build().map_err(Error::RequestError)
|
||||
}
|
||||
}
|
||||
|
||||
fn headers(
|
||||
content_type: Option<&str>,
|
||||
token: Option<&Token>
|
||||
) -> Result<HeaderMap, Error> {
|
||||
fn headers(content_type: Option<&str>, token: Option<&Token>) -> Result<HeaderMap, Error> {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
USER_AGENT,
|
||||
HeaderValue::from_str("krill")?
|
||||
);
|
||||
headers.insert(USER_AGENT, HeaderValue::from_str("krill")?);
|
||||
if let Some(content_type) = content_type {
|
||||
headers.insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_str(content_type)?
|
||||
);
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_str(content_type)?);
|
||||
}
|
||||
if let Some(token) = token {
|
||||
headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&format!("Bearer {}", token))?
|
||||
HeaderValue::from_str(&format!("Bearer {}", token))?,
|
||||
);
|
||||
}
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
fn process_json_response<T: DeserializeOwned>(
|
||||
res: Response
|
||||
) -> Result<T, Error> {
|
||||
fn process_json_response<T: DeserializeOwned>(res: Response) -> Result<T, Error> {
|
||||
match opt_text_response(res) {
|
||||
Err(e) => Err(e),
|
||||
Ok(None) => Err(Error::EmptyResponse),
|
||||
@@ -195,31 +159,27 @@ fn process_json_response<T: DeserializeOwned>(
|
||||
|
||||
fn opt_text_response(mut res: Response) -> Result<Option<String>, Error> {
|
||||
match res.status() {
|
||||
StatusCode::OK => {
|
||||
match res.text().ok() {
|
||||
None => Ok(None),
|
||||
Some(s) => {
|
||||
if s.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(s))
|
||||
}
|
||||
StatusCode::OK => match res.text().ok() {
|
||||
None => Ok(None),
|
||||
Some(s) => {
|
||||
if s.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(s))
|
||||
}
|
||||
}
|
||||
},
|
||||
StatusCode::FORBIDDEN => Err(Error::Forbidden),
|
||||
status => {
|
||||
match res.text() {
|
||||
Ok(body) => {
|
||||
if body.is_empty() {
|
||||
Err(Error::BadStatus(status))
|
||||
} else {
|
||||
Err(Error::wrap_err_res(status, body))
|
||||
}
|
||||
},
|
||||
_ => Err(Error::BadStatus(status))
|
||||
status => match res.text() {
|
||||
Ok(body) => {
|
||||
if body.is_empty() {
|
||||
Err(Error::BadStatus(status))
|
||||
} else {
|
||||
Err(Error::wrap_err_res(status, body))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Err(Error::BadStatus(status)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,51 +187,57 @@ fn opt_text_response(mut res: Response) -> Result<Option<String>, Error> {
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
#[display(fmt="Request Error: {}", _0)]
|
||||
#[display(fmt = "Request Error: {}", _0)]
|
||||
RequestError(reqwest::Error),
|
||||
|
||||
#[display(fmt="Access Forbidden")]
|
||||
#[display(fmt = "Access Forbidden")]
|
||||
Forbidden,
|
||||
|
||||
#[display(fmt="Received bad status: {}", _0)]
|
||||
#[display(fmt = "Received bad status: {}", _0)]
|
||||
BadStatus(StatusCode),
|
||||
|
||||
#[display(fmt="Status: {}, Error: {}", _0, _1)]
|
||||
#[display(fmt = "Status: {}, Error: {}", _0, _1)]
|
||||
ErrorWithBody(StatusCode, String),
|
||||
|
||||
#[display(fmt="Status: {}, Error: {}", _0, _1)]
|
||||
#[display(fmt = "Status: {}, Error: {}", _0, _1)]
|
||||
ErrorWithJson(StatusCode, ErrorResponse),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
JsonError(serde_json::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
InvalidHeader(InvalidHeaderValue),
|
||||
|
||||
#[display(fmt="Empty response received from server")]
|
||||
#[display(fmt = "Empty response received from server")]
|
||||
EmptyResponse,
|
||||
|
||||
#[display(fmt="Unexpected response: {}", _0)]
|
||||
UnexpectedResponse(String)
|
||||
#[display(fmt = "Unexpected response: {}", _0)]
|
||||
UnexpectedResponse(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn wrap_err_res(code: StatusCode, content: String) -> Error {
|
||||
match serde_json::from_str::<ErrorResponse>(&content) {
|
||||
Ok(res) => Error::ErrorWithJson(code, res),
|
||||
Err(_) => Error::ErrorWithBody(code, content)
|
||||
Err(_) => Error::ErrorWithBody(code, content),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for Error {
|
||||
fn from(e: reqwest::Error) -> Self { Error::RequestError(e) }
|
||||
fn from(e: reqwest::Error) -> Self {
|
||||
Error::RequestError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(e: serde_json::Error) -> Self { Error::JsonError(e) }
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
Error::JsonError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InvalidHeaderValue> for Error {
|
||||
fn from(v: InvalidHeaderValue) -> Self { Error::InvalidHeader(v) }
|
||||
fn from(v: InvalidHeaderValue) -> Self {
|
||||
Error::InvalidHeader(v)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,14 +1,14 @@
|
||||
//! General utility modules for use all over the code base
|
||||
use std::time::Duration;
|
||||
use bytes::Bytes;
|
||||
use chrono::offset::TimeZone;
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use chrono::offset::TimeZone;
|
||||
use rpki::crypto::DigestAlgorithm;
|
||||
use serde::Deserialize;
|
||||
use serde::Deserializer;
|
||||
use serde::Serialize;
|
||||
use serde::Serializer;
|
||||
use serde::Deserializer;
|
||||
use serde::Deserialize;
|
||||
use std::time::Duration;
|
||||
|
||||
pub mod ext_serde;
|
||||
pub mod file;
|
||||
@@ -42,20 +42,20 @@ impl Time {
|
||||
}
|
||||
|
||||
impl Serialize for Time {
|
||||
fn serialize<S>(
|
||||
&self, serializer: S
|
||||
) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_i64(self.0.timestamp_millis())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Time {
|
||||
fn deserialize<D>(
|
||||
deserializer: D
|
||||
) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
||||
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let timestamp: i64 = i64::deserialize(deserializer)?;
|
||||
Ok(Time(Utc.timestamp_millis(timestamp)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
//! Support for signing things using software keys (through openssl) and
|
||||
//! storing them unencrypted on disk.
|
||||
use std::{fs, io};
|
||||
use std::path::PathBuf;
|
||||
use bytes::Bytes;
|
||||
use openssl::rsa::Rsa;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::{PKey, PKeyRef, Private};
|
||||
use rpki::crypto::{
|
||||
Signature,
|
||||
SignatureAlgorithm,
|
||||
Signer,
|
||||
SigningError,
|
||||
PublicKey,
|
||||
PublicKeyFormat
|
||||
};
|
||||
use openssl::rsa::Rsa;
|
||||
use rpki::crypto::signer::KeyError;
|
||||
use rpki::crypto::{
|
||||
PublicKey, PublicKeyFormat, Signature, SignatureAlgorithm, Signer, SigningError,
|
||||
};
|
||||
use serde::{de, ser};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
|
||||
//------------ SignerKeyId ---------------------------------------------------
|
||||
|
||||
@@ -40,24 +34,24 @@ impl AsRef<str> for SignerKeyId {
|
||||
}
|
||||
|
||||
impl Serialize for SignerKeyId {
|
||||
fn serialize<S>(
|
||||
&self,
|
||||
serializer: S
|
||||
) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.as_ref().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SignerKeyId {
|
||||
fn deserialize<D>(
|
||||
deserializer: D
|
||||
) -> Result<Self, D::Error> where D: Deserializer<'de> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Ok(SignerKeyId::new(&s))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OpenSslSigner -------------------------------------------------
|
||||
|
||||
/// An openssl based signer.
|
||||
@@ -65,21 +59,20 @@ impl<'de> Deserialize<'de> for SignerKeyId {
|
||||
/// Keeps the keys in memory (for now).
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct OpenSslSigner {
|
||||
keys_dir: PathBuf
|
||||
keys_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl OpenSslSigner {
|
||||
pub fn build(work_dir: &PathBuf) -> Result<Self, SignerError> {
|
||||
let meta_data = fs::metadata(&work_dir)?;
|
||||
if meta_data.is_dir() {
|
||||
|
||||
let mut keys_dir = PathBuf::from(work_dir);
|
||||
keys_dir.push("keys");
|
||||
if ! keys_dir.is_dir() {
|
||||
if !keys_dir.is_dir() {
|
||||
fs::create_dir_all(&keys_dir)?;
|
||||
}
|
||||
|
||||
Ok(OpenSslSigner { keys_dir } )
|
||||
Ok(OpenSslSigner { keys_dir })
|
||||
} else {
|
||||
Err(SignerError::InvalidWorkDir(work_dir.clone()))
|
||||
}
|
||||
@@ -89,17 +82,15 @@ impl OpenSslSigner {
|
||||
impl OpenSslSigner {
|
||||
fn sign_with_key<D: AsRef<[u8]> + ?Sized>(
|
||||
pkey: &PKeyRef<Private>,
|
||||
data: &D
|
||||
data: &D,
|
||||
) -> Result<Signature, SignerError> {
|
||||
let mut signer = ::openssl::sign::Signer::new(
|
||||
MessageDigest::sha256(),
|
||||
pkey
|
||||
)?;
|
||||
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), pkey)?;
|
||||
signer.update(data.as_ref())?;
|
||||
|
||||
let signature = Signature::new(
|
||||
SignatureAlgorithm::default(),
|
||||
Bytes::from(signer.sign_to_vec()?));
|
||||
Bytes::from(signer.sign_to_vec()?),
|
||||
);
|
||||
|
||||
Ok(signature)
|
||||
}
|
||||
@@ -113,7 +104,6 @@ impl OpenSslSigner {
|
||||
} else {
|
||||
Err(SignerError::KeyNotFound)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn key_path(&self, key_id: &SignerKeyId) -> PathBuf {
|
||||
@@ -124,14 +114,10 @@ impl OpenSslSigner {
|
||||
}
|
||||
|
||||
impl Signer for OpenSslSigner {
|
||||
|
||||
type KeyId = SignerKeyId;
|
||||
type Error = SignerError;
|
||||
|
||||
fn create_key(
|
||||
&mut self,
|
||||
_algorithm: PublicKeyFormat
|
||||
) -> Result<Self::KeyId, Self::Error> {
|
||||
fn create_key(&mut self, _algorithm: PublicKeyFormat) -> Result<Self::KeyId, Self::Error> {
|
||||
let kp = OpenSslKeyPair::build()?;
|
||||
|
||||
let pk = &kp.subject_public_key_info()?;
|
||||
@@ -147,18 +133,12 @@ impl Signer for OpenSslSigner {
|
||||
Ok(key_id)
|
||||
}
|
||||
|
||||
fn get_key_info(
|
||||
&self,
|
||||
key_id: &Self::KeyId
|
||||
) -> Result<PublicKey, KeyError<Self::Error>> {
|
||||
fn get_key_info(&self, key_id: &Self::KeyId) -> Result<PublicKey, KeyError<Self::Error>> {
|
||||
let key_pair = self.load_key(key_id)?;
|
||||
Ok(key_pair.subject_public_key_info()?)
|
||||
}
|
||||
|
||||
fn destroy_key(
|
||||
&mut self,
|
||||
key_id: &Self::KeyId
|
||||
) -> Result<(), KeyError<Self::Error>> {
|
||||
fn destroy_key(&mut self, key_id: &Self::KeyId) -> Result<(), KeyError<Self::Error>> {
|
||||
let path = self.key_path(key_id);
|
||||
if path.exists() {
|
||||
fs::remove_file(path).map_err(SignerError::IoError)?;
|
||||
@@ -170,24 +150,20 @@ impl Signer for OpenSslSigner {
|
||||
&self,
|
||||
key_id: &Self::KeyId,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
data: &D
|
||||
data: &D,
|
||||
) -> Result<Signature, SigningError<Self::Error>> {
|
||||
let key_pair = self.load_key(key_id)?;
|
||||
Self::sign_with_key(key_pair.pkey.as_ref(), data)
|
||||
.map_err(|e| { SigningError::Signer(e)})
|
||||
Self::sign_with_key(key_pair.pkey.as_ref(), data).map_err(|e| SigningError::Signer(e))
|
||||
}
|
||||
|
||||
fn sign_one_off<D: AsRef<[u8]> + ?Sized>(
|
||||
&self,
|
||||
_algorithm: SignatureAlgorithm,
|
||||
data: &D
|
||||
data: &D,
|
||||
) -> Result<(Signature, PublicKey), SignerError> {
|
||||
let kp = OpenSslKeyPair::build()?;
|
||||
|
||||
let signature = Self::sign_with_key(
|
||||
kp.pkey.as_ref(),
|
||||
data
|
||||
)?;
|
||||
let signature = Self::sign_with_key(kp.pkey.as_ref(), data)?;
|
||||
|
||||
let key = kp.subject_public_key_info()?;
|
||||
|
||||
@@ -199,21 +175,22 @@ impl Signer for OpenSslSigner {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OpenSslKeyPair ------------------------------------------------
|
||||
|
||||
/// An openssl based RSA key pair
|
||||
pub struct OpenSslKeyPair {
|
||||
pkey: PKey<Private>
|
||||
pkey: PKey<Private>,
|
||||
}
|
||||
|
||||
impl Serialize for OpenSslKeyPair {
|
||||
fn serialize<S>(
|
||||
&self,
|
||||
s: S
|
||||
) -> Result<S::Ok, S::Error> where
|
||||
S: Serializer {
|
||||
let bytes: Vec<u8> = self.pkey.as_ref().private_key_to_der()
|
||||
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let bytes: Vec<u8> = self
|
||||
.pkey
|
||||
.as_ref()
|
||||
.private_key_to_der()
|
||||
.map_err(ser::Error::custom)?;
|
||||
|
||||
base64::encode(&bytes).serialize(s)
|
||||
@@ -221,25 +198,19 @@ impl Serialize for OpenSslKeyPair {
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for OpenSslKeyPair {
|
||||
fn deserialize<D>(
|
||||
d: D
|
||||
) -> Result<OpenSslKeyPair, D::Error> where
|
||||
D: Deserializer<'de> {
|
||||
fn deserialize<D>(d: D) -> Result<OpenSslKeyPair, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
match String::deserialize(d) {
|
||||
Ok(base64) => {
|
||||
let bytes = base64::decode(&base64)
|
||||
.map_err(de::Error::custom)?;
|
||||
let bytes = base64::decode(&base64).map_err(de::Error::custom)?;
|
||||
|
||||
let pkey = PKey::private_key_from_der(&bytes)
|
||||
.map_err(de::Error::custom)?;
|
||||
let pkey = PKey::private_key_from_der(&bytes).map_err(de::Error::custom)?;
|
||||
|
||||
Ok(
|
||||
OpenSslKeyPair {
|
||||
pkey
|
||||
}
|
||||
)
|
||||
},
|
||||
Err(err) => Err(err)
|
||||
Ok(OpenSslKeyPair { pkey })
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +221,7 @@ impl OpenSslKeyPair {
|
||||
// So, there is no way to recover.
|
||||
let rsa = Rsa::generate(2048)?;
|
||||
let pkey = PKey::from_rsa(rsa)?;
|
||||
Ok(OpenSslKeyPair{ pkey })
|
||||
Ok(OpenSslKeyPair { pkey })
|
||||
}
|
||||
|
||||
fn subject_public_key_info(&self) -> Result<PublicKey, SignerError> {
|
||||
@@ -261,7 +232,6 @@ impl OpenSslKeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ OpenSslKeyError -----------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -323,7 +293,6 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_serialize_and_deserialize_key() {
|
||||
|
||||
let key = OpenSslKeyPair::build().unwrap();
|
||||
let json = serde_json::to_string(&key).unwrap();
|
||||
let key_des: OpenSslKeyPair = serde_json::from_str(json.as_str()).unwrap();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use bytes::Bytes;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rpki::uri;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use bytes::Bytes;
|
||||
use rand::{thread_rng, Rng};
|
||||
use rpki::uri;
|
||||
|
||||
/// This method sets up a test directory with a random name (a number)
|
||||
/// under 'work', relative to where cargo is running. It then runs the
|
||||
@@ -13,7 +13,10 @@ use rpki::uri;
|
||||
/// directory.
|
||||
///
|
||||
/// Note that if your test fails the directory is not cleaned up.
|
||||
pub fn test_under_tmp<F>(op: F) where F: FnOnce(PathBuf) -> () {
|
||||
pub fn test_under_tmp<F>(op: F)
|
||||
where
|
||||
F: FnOnce(PathBuf) -> (),
|
||||
{
|
||||
let dir = sub_dir(&PathBuf::from("work"));
|
||||
let path = PathBuf::from(&dir);
|
||||
|
||||
@@ -41,13 +44,17 @@ pub fn rsync(s: &str) -> uri::Rsync {
|
||||
uri::Rsync::from_str(s).unwrap()
|
||||
}
|
||||
|
||||
pub fn https(s: &str) -> uri::Https { uri::Https::from_str(s).unwrap() }
|
||||
pub fn https(s: &str) -> uri::Https {
|
||||
uri::Https::from_str(s).unwrap()
|
||||
}
|
||||
|
||||
pub fn as_bytes(s: &str) -> Bytes { Bytes::from(s) }
|
||||
pub fn as_bytes(s: &str) -> Bytes {
|
||||
Bytes::from(s)
|
||||
}
|
||||
|
||||
pub fn save_file(base_dir: &PathBuf, file_name: &str, content: &[u8]) {
|
||||
let mut full_name = base_dir.clone();
|
||||
full_name.push(PathBuf::from(file_name));
|
||||
let mut f = File::create(full_name).unwrap();
|
||||
f.write_all(content).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
+93
-113
@@ -1,17 +1,16 @@
|
||||
//! Support for RPKI XML structures.
|
||||
use std::{fs, io};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use base64;
|
||||
use base64::DecodeError;
|
||||
use bytes::Bytes;
|
||||
use hex;
|
||||
use hex::FromHexError;
|
||||
use xmlrs::{reader, writer};
|
||||
use xmlrs::{EmitterConfig, EventReader, EventWriter, ParserConfig};
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::{fs, io};
|
||||
use xmlrs::attribute::OwnedAttribute;
|
||||
use xmlrs::reader::XmlEvent;
|
||||
|
||||
use xmlrs::{reader, writer};
|
||||
use xmlrs::{EmitterConfig, EventReader, EventWriter, ParserConfig};
|
||||
|
||||
//------------ XmlReader -----------------------------------------------------
|
||||
|
||||
@@ -28,20 +27,18 @@ pub struct XmlReader<R: io::Read> {
|
||||
cached_event: Option<XmlEvent>,
|
||||
|
||||
/// Name of the next start element, if any
|
||||
next_start_name: Option<String>
|
||||
next_start_name: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
/// Reader methods
|
||||
impl <R: io::Read> XmlReader<R> {
|
||||
|
||||
impl<R: io::Read> XmlReader<R> {
|
||||
/// Gets the next XmlEvent
|
||||
///
|
||||
/// Will take cached event if there is one
|
||||
fn next(&mut self) -> Result<XmlEvent, XmlReaderErr> {
|
||||
match self.cached_event.take() {
|
||||
Some(e) => Ok(e),
|
||||
None => Ok(self.reader.next()?)
|
||||
None => Ok(self.reader.next()?),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,42 +48,46 @@ impl <R: io::Read> XmlReader<R> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Basic operations to parse the XML.
|
||||
///
|
||||
/// These methods are private because they are used by the higher level
|
||||
/// closure based methods, defined below, that one should use to parse
|
||||
/// XML safely.
|
||||
impl <R: io::Read> XmlReader<R> {
|
||||
impl<R: io::Read> XmlReader<R> {
|
||||
/// Takes the next element and expects a start of document.
|
||||
fn start_document(&mut self) -> Result<(), XmlReaderErr> {
|
||||
match self.next() {
|
||||
Ok(reader::XmlEvent::StartDocument {..}) => Ok(()),
|
||||
_ => Err(XmlReaderErr::ExpectedStartDocument)
|
||||
Ok(reader::XmlEvent::StartDocument { .. }) => Ok(()),
|
||||
_ => Err(XmlReaderErr::ExpectedStartDocument),
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the next element and expects a start element with the given name.
|
||||
fn expect_element(&mut self) -> Result<(Tag, Attributes), XmlReaderErr> {
|
||||
match self.next() {
|
||||
Ok(reader::XmlEvent::StartElement { name, attributes, ..}) => {
|
||||
Ok((Tag{name: name.local_name}, Attributes{attributes}))
|
||||
},
|
||||
_ => Err(XmlReaderErr::ExpectedStart)
|
||||
Ok(reader::XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
}) => Ok((
|
||||
Tag {
|
||||
name: name.local_name,
|
||||
},
|
||||
Attributes { attributes },
|
||||
)),
|
||||
_ => Err(XmlReaderErr::ExpectedStart),
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the next element and expects a close element with the given name.
|
||||
fn expect_close(&mut self, tag: Tag) -> Result<(), XmlReaderErr> {
|
||||
match self.next() {
|
||||
Ok(reader::XmlEvent::EndElement { name, ..}) => {
|
||||
Ok(reader::XmlEvent::EndElement { name, .. }) => {
|
||||
if name.local_name == tag.name {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(XmlReaderErr::ExpectedClose(tag.name))
|
||||
}
|
||||
}
|
||||
_ => Err(XmlReaderErr::ExpectedClose(tag.name))
|
||||
_ => Err(XmlReaderErr::ExpectedClose(tag.name)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ impl <R: io::Read> XmlReader<R> {
|
||||
fn end_document(&mut self) -> Result<(), XmlReaderErr> {
|
||||
match self.next() {
|
||||
Ok(reader::XmlEvent::EndDocument) => Ok(()),
|
||||
_ => Err(XmlReaderErr::ExpectedEnd)
|
||||
_ => Err(XmlReaderErr::ExpectedEnd),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,23 +109,25 @@ impl <R: io::Read> XmlReader<R> {
|
||||
/// content (such as Characters), and process the enclosed content. In
|
||||
/// particular it ensures that the consumer cannot accidentally get close
|
||||
/// tags - so it forces that execution returns.
|
||||
impl <R: io::Read> XmlReader<R> {
|
||||
impl<R: io::Read> XmlReader<R> {
|
||||
/// Decodes an XML structure
|
||||
///
|
||||
/// This method checks that the document starts, then passes a reader
|
||||
/// instance to the provided closure, and will return the result from
|
||||
/// that after checking that the XML document is fully processed.
|
||||
pub fn decode<F, T, E>(source: R, op: F) -> Result<T, E>
|
||||
where F: FnOnce(&mut Self) -> Result<T, E>,
|
||||
E: From<XmlReaderErr> {
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<T, E>,
|
||||
E: From<XmlReaderErr>,
|
||||
{
|
||||
let mut config = ParserConfig::new();
|
||||
config.trim_whitespace = true;
|
||||
config.ignore_comments = true;
|
||||
|
||||
let mut xml = XmlReader{
|
||||
let mut xml = XmlReader {
|
||||
reader: config.create_reader(source),
|
||||
cached_event: None,
|
||||
next_start_name: None
|
||||
next_start_name: None,
|
||||
};
|
||||
|
||||
xml.start_document()?;
|
||||
@@ -141,8 +144,10 @@ impl <R: io::Read> XmlReader<R> {
|
||||
/// the closure completes it will verify that the next element is the
|
||||
/// Close Element for this Tag, and returns the result from the closure.
|
||||
pub fn take_element<F, T, E>(&mut self, op: F) -> Result<T, E>
|
||||
where F: FnOnce(&Tag, Attributes, &mut Self) -> Result<T, E>,
|
||||
E: From<XmlReaderErr> {
|
||||
where
|
||||
F: FnOnce(&Tag, Attributes, &mut Self) -> Result<T, E>,
|
||||
E: From<XmlReaderErr>,
|
||||
{
|
||||
let (tag, attr) = self.expect_element()?;
|
||||
let res = op(&tag, attr, self)?;
|
||||
self.expect_close(tag)?;
|
||||
@@ -153,20 +158,15 @@ impl <R: io::Read> XmlReader<R> {
|
||||
///
|
||||
/// Checks that the element has the expected name and passed the closure
|
||||
/// to the generic take_element method.
|
||||
pub fn take_named_element<F, T, E>(
|
||||
&mut self,
|
||||
name: &str,
|
||||
op: F
|
||||
) -> Result<T, E>
|
||||
pub fn take_named_element<F, T, E>(&mut self, name: &str, op: F) -> Result<T, E>
|
||||
where
|
||||
F: FnOnce(Attributes, &mut Self) -> Result<T, E>,
|
||||
E: From<XmlReaderErr>
|
||||
E: From<XmlReaderErr>,
|
||||
{
|
||||
self.take_element(|t, a, r| {
|
||||
if t.name != name {
|
||||
Err(XmlReaderErr::ExpectedNamedStart(name.to_string()).into())
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
op(a, r)
|
||||
}
|
||||
})
|
||||
@@ -184,21 +184,22 @@ impl <R: io::Read> XmlReader<R> {
|
||||
/// that a 'take_*' method with a closure was used for the parent element,
|
||||
/// then we will get a clear error there (expect end element).
|
||||
pub fn take_opt_element<F, T, E>(&mut self, op: F) -> Result<Option<T>, E>
|
||||
where F: FnOnce(&Tag, Attributes, &mut Self) -> Result<Option<T>, E>,
|
||||
E: From<XmlReaderErr> {
|
||||
|
||||
where
|
||||
F: FnOnce(&Tag, Attributes, &mut Self) -> Result<Option<T>, E>,
|
||||
E: From<XmlReaderErr>,
|
||||
{
|
||||
let n = self.next()?;
|
||||
match n {
|
||||
XmlEvent::StartElement { name, attributes, ..} => {
|
||||
let tag = Tag{name: name.local_name};
|
||||
let res = op(
|
||||
&tag,
|
||||
Attributes{attributes},
|
||||
self
|
||||
)?;
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
let tag = Tag {
|
||||
name: name.local_name,
|
||||
};
|
||||
let res = op(&tag, Attributes { attributes }, self)?;
|
||||
self.expect_close(tag)?;
|
||||
Ok(res)
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
self.cache(n);
|
||||
Ok(None)
|
||||
@@ -209,10 +210,8 @@ impl <R: io::Read> XmlReader<R> {
|
||||
/// Takes characters
|
||||
pub fn take_chars(&mut self) -> Result<String, XmlReaderErr> {
|
||||
match self.next() {
|
||||
Ok(reader::XmlEvent::Characters(chars)) => {
|
||||
Ok(chars)
|
||||
}
|
||||
_ => Err(XmlReaderErr::ExpectedCharacters)
|
||||
Ok(reader::XmlEvent::Characters(chars)) => Ok(chars),
|
||||
_ => Err(XmlReaderErr::ExpectedCharacters),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,15 +225,13 @@ impl <R: io::Read> XmlReader<R> {
|
||||
self.take_bytes(base64::URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
fn take_bytes(
|
||||
&mut self,
|
||||
config: base64::Config
|
||||
) -> Result<Bytes, XmlReaderErr> {
|
||||
fn take_bytes(&mut self, config: base64::Config) -> Result<Bytes, XmlReaderErr> {
|
||||
let chars = self.take_chars()?;
|
||||
// strip whitespace and padding (we are liberal in what we accept here)
|
||||
// TODO: Avoid allocation, pass in an AsRef<[u8]> that
|
||||
// removes any whitespace on the fly.
|
||||
let chars: Vec<u8> = chars.into_bytes()
|
||||
let chars: Vec<u8> = chars
|
||||
.into_bytes()
|
||||
.into_iter()
|
||||
.filter(|c| !b" \n\t\r\x0b\x0c=".contains(c))
|
||||
.collect();
|
||||
@@ -243,7 +240,6 @@ impl <R: io::Read> XmlReader<R> {
|
||||
Ok(Bytes::from(b64))
|
||||
}
|
||||
|
||||
|
||||
pub fn take_empty(&mut self) -> Result<(), XmlReaderErr> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -254,8 +250,8 @@ impl <R: io::Read> XmlReader<R> {
|
||||
pub fn next_start_name(&mut self) -> Option<&str> {
|
||||
match self.next() {
|
||||
Err(_) => None,
|
||||
Ok(e) => {
|
||||
if let XmlEvent::StartElement { ref name, ..} = e {
|
||||
Ok(e) => {
|
||||
if let XmlEvent::StartElement { ref name, .. } = e {
|
||||
// XXX not the most efficient.. but need a different
|
||||
// underlying XML parser to get around ownership
|
||||
// issues.
|
||||
@@ -271,12 +267,13 @@ impl <R: io::Read> XmlReader<R> {
|
||||
}
|
||||
|
||||
impl XmlReader<fs::File> {
|
||||
|
||||
/// Opens a file and decodes it as an XML file.
|
||||
pub fn open<P, F, T, E>(path: P, op: F) -> Result<T, E>
|
||||
where F: FnOnce(&mut Self) -> Result<T, E>,
|
||||
P: AsRef<Path>,
|
||||
E: From<XmlReaderErr> + From<io::Error> {
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<T, E>,
|
||||
P: AsRef<Path>,
|
||||
E: From<XmlReaderErr> + From<io::Error>,
|
||||
{
|
||||
Self::decode(fs::File::open(path)?, op)
|
||||
}
|
||||
}
|
||||
@@ -313,11 +310,11 @@ pub enum XmlReaderErr {
|
||||
ReaderError(reader::Error),
|
||||
|
||||
#[display(fmt = "Base64 decoding issue: {}", _0)]
|
||||
Base64Error(DecodeError)
|
||||
Base64Error(DecodeError),
|
||||
}
|
||||
|
||||
impl From<io::Error> for XmlReaderErr {
|
||||
fn from(e: io::Error) -> XmlReaderErr{
|
||||
fn from(e: io::Error) -> XmlReaderErr {
|
||||
XmlReaderErr::IoError(e)
|
||||
}
|
||||
}
|
||||
@@ -340,26 +337,27 @@ impl From<DecodeError> for XmlReaderErr {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Attributes ----------------------------------------------------
|
||||
|
||||
/// A convenient wrapper for XML tag attributes
|
||||
pub struct Attributes {
|
||||
/// The underlying xml-rs structure
|
||||
attributes: Vec<OwnedAttribute>
|
||||
attributes: Vec<OwnedAttribute>,
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
|
||||
/// Takes an optional attribute by name
|
||||
pub fn take_opt(&mut self, name: &str) -> Option<String> {
|
||||
let i = self.attributes.iter().position(|a| a.name.local_name == name);
|
||||
let i = self
|
||||
.attributes
|
||||
.iter()
|
||||
.position(|a| a.name.local_name == name);
|
||||
match i {
|
||||
Some(i) => {
|
||||
let a = self.attributes.swap_remove(i);
|
||||
Some(a.value)
|
||||
}
|
||||
None => None
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,12 +373,10 @@ impl Attributes {
|
||||
}
|
||||
|
||||
/// Takes a required hexencoded attribute and converts it to Bytes
|
||||
pub fn take_req_hex(&mut self, name: &str)
|
||||
-> Result<Bytes, AttributesError> {
|
||||
|
||||
pub fn take_req_hex(&mut self, name: &str) -> Result<Bytes, AttributesError> {
|
||||
match hex::decode(self.take_req(name)?) {
|
||||
Err(e) => Err(AttributesError::HexError(e)),
|
||||
Ok(b) => Ok(Bytes::from(b))
|
||||
Ok(b) => Ok(Bytes::from(b)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +390,6 @@ impl Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AttributesError -----------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -406,7 +401,7 @@ pub enum AttributesError {
|
||||
ExtraAttributes(String),
|
||||
|
||||
#[display(fmt = "Wrong hex encoding: {}", _0)]
|
||||
HexError(FromHexError)
|
||||
HexError(FromHexError),
|
||||
}
|
||||
|
||||
impl AttributesError {
|
||||
@@ -417,14 +412,12 @@ impl AttributesError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Tag -----------------------------------------------------------
|
||||
|
||||
pub struct Tag {
|
||||
pub name: String
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
//------------ XmlWriter -----------------------------------------------------
|
||||
|
||||
/// A convenience wrapper for RPKI XML generation
|
||||
@@ -432,14 +425,11 @@ pub struct Tag {
|
||||
/// This type only exposes things we need for the RPKI XML structures.
|
||||
pub struct XmlWriter<W> {
|
||||
/// The underlying xml-rs writer
|
||||
writer: EventWriter<W>
|
||||
writer: EventWriter<W>,
|
||||
}
|
||||
|
||||
|
||||
/// Generate the XML.
|
||||
impl <W: io::Write> XmlWriter<W> {
|
||||
|
||||
|
||||
impl<W: io::Write> XmlWriter<W> {
|
||||
fn unwrap_emitter_error<T>(r: Result<T, writer::Error>) -> Result<T, io::Error> {
|
||||
match r {
|
||||
Ok(t) => Ok(t),
|
||||
@@ -463,8 +453,11 @@ impl <W: io::Write> XmlWriter<W> {
|
||||
&mut self,
|
||||
name: &str,
|
||||
attr: Option<&[(&str, &str)]>,
|
||||
op: F) -> Result<(), io::Error>
|
||||
where F: FnOnce(&mut Self) -> Result<(), io::Error> {
|
||||
op: F,
|
||||
) -> Result<(), io::Error>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<(), io::Error>,
|
||||
{
|
||||
let mut start = writer::XmlEvent::start_element(name);
|
||||
|
||||
if let Some(v) = attr {
|
||||
@@ -475,18 +468,14 @@ impl <W: io::Write> XmlWriter<W> {
|
||||
|
||||
Self::unwrap_emitter_error(self.writer.write(start))?;
|
||||
op(self)?;
|
||||
Self::unwrap_emitter_error(
|
||||
self.writer.write(writer::XmlEvent::end_element())
|
||||
)?;
|
||||
Self::unwrap_emitter_error(self.writer.write(writer::XmlEvent::end_element()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Puts some String in a characters element
|
||||
pub fn put_text(&mut self, text: &str) -> Result<(), io::Error> {
|
||||
Self::unwrap_emitter_error(
|
||||
self.writer.write(writer::XmlEvent::Characters(text))
|
||||
)?;
|
||||
Self::unwrap_emitter_error(self.writer.write(writer::XmlEvent::Characters(text)))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -516,8 +505,9 @@ impl <W: io::Write> XmlWriter<W> {
|
||||
/// method, and in future others like it, to set up the writer for a
|
||||
/// specific type (Vec<u8>, File, etc.).
|
||||
fn encode<F>(w: W, op: F) -> Result<(), io::Error>
|
||||
where F: FnOnce(&mut Self) -> Result<(), io::Error> {
|
||||
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<(), io::Error>,
|
||||
{
|
||||
let writer = EmitterConfig::new()
|
||||
.write_document_declaration(false)
|
||||
.normalize_empty_elements(true)
|
||||
@@ -531,11 +521,10 @@ impl <W: io::Write> XmlWriter<W> {
|
||||
}
|
||||
|
||||
impl XmlWriter<()> {
|
||||
|
||||
/// Call this to encode XML into a Vec<u8>
|
||||
pub fn encode_vec<F>(op: F) -> Vec<u8>
|
||||
where F: FnOnce(&mut XmlWriter<&mut Vec<u8>>)
|
||||
-> Result<(), io::Error>
|
||||
where
|
||||
F: FnOnce(&mut XmlWriter<&mut Vec<u8>>) -> Result<(), io::Error>,
|
||||
{
|
||||
let mut b = Vec::new();
|
||||
XmlWriter::encode(&mut b, op).unwrap(); // IO error impossible for vec
|
||||
@@ -543,12 +532,13 @@ impl XmlWriter<()> {
|
||||
}
|
||||
|
||||
pub fn encode_to_file<F>(file: &mut File, op: F) -> Result<(), io::Error>
|
||||
where F: FnOnce(&mut XmlWriter<&mut File>) -> Result<(), io::Error> {
|
||||
where
|
||||
F: FnOnce(&mut XmlWriter<&mut File>) -> Result<(), io::Error>,
|
||||
{
|
||||
XmlWriter::encode(file, op)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -559,20 +549,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_write_xml() {
|
||||
|
||||
let xml = XmlWriter::encode_vec(|w| {
|
||||
w.put_element(
|
||||
"a",
|
||||
Some(&[
|
||||
("xmlns", "http://ns/"),
|
||||
("c", "d")
|
||||
]),
|
||||
|w| {
|
||||
w.put_element("b", None, |w| {
|
||||
w.put_base64_std(&Bytes::from("X"))
|
||||
})
|
||||
}
|
||||
)
|
||||
w.put_element("a", Some(&[("xmlns", "http://ns/"), ("c", "d")]), |w| {
|
||||
w.put_element("b", None, |w| w.put_base64_std(&Bytes::from("X")))
|
||||
})
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
extern crate ignore;
|
||||
|
||||
use std::process::Command;
|
||||
use ignore::Walk;
|
||||
use std::process::Command;
|
||||
|
||||
//#[allow(dead_code)]
|
||||
fn main() {
|
||||
@@ -11,4 +11,4 @@ fn main() {
|
||||
}
|
||||
}
|
||||
Command::new("./build-dist.sh").status().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-40
@@ -1,19 +1,8 @@
|
||||
//! Authorization for the API
|
||||
use actix_web::{
|
||||
Error,
|
||||
FromRequest,
|
||||
HttpResponse,
|
||||
HttpRequest,
|
||||
ResponseError,
|
||||
};
|
||||
use actix_web::dev::{
|
||||
Payload,
|
||||
};
|
||||
use actix_identity::Identity;
|
||||
use actix_web::web::{
|
||||
self,
|
||||
Json
|
||||
};
|
||||
use actix_web::dev::Payload;
|
||||
use actix_web::web::{self, Json};
|
||||
use actix_web::{Error, FromRequest, HttpRequest, HttpResponse, ResponseError};
|
||||
|
||||
use krill_commons::api::admin::Token;
|
||||
|
||||
@@ -22,20 +11,19 @@ use std::fmt;
|
||||
|
||||
pub const AUTH_COOKIE_NAME: &str = "krill_auth";
|
||||
|
||||
|
||||
//------------ Authorizer ----------------------------------------------------
|
||||
|
||||
/// This type is responsible for checking authorisations when the API is
|
||||
/// accessed.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Authorizer {
|
||||
krill_auth_token: Token
|
||||
krill_auth_token: Token,
|
||||
}
|
||||
|
||||
impl Authorizer {
|
||||
pub fn new(krill_auth_token: &Token) -> Self {
|
||||
Authorizer {
|
||||
krill_auth_token: krill_auth_token.clone()
|
||||
krill_auth_token: krill_auth_token.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,19 +32,14 @@ impl Authorizer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Credentials ---------------------------------------------------
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Credentials {
|
||||
token: Token
|
||||
token: Token,
|
||||
}
|
||||
|
||||
pub fn login(
|
||||
server: web::Data<AppServer>,
|
||||
cred: Json<Credentials>,
|
||||
id: Identity
|
||||
) -> HttpResponse {
|
||||
pub fn login(server: web::Data<AppServer>, cred: Json<Credentials>, id: Identity) -> HttpResponse {
|
||||
if server.read().login(cred.token.clone()) {
|
||||
id.remember("admin".to_string());
|
||||
HttpResponse::Ok().finish()
|
||||
@@ -66,20 +49,15 @@ pub fn login(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logout(
|
||||
id: Identity
|
||||
) -> HttpResponse {
|
||||
pub fn logout(id: Identity) -> HttpResponse {
|
||||
id.forget();
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
||||
pub fn is_logged_in(
|
||||
_auth: Auth
|
||||
) -> HttpResponse {
|
||||
pub fn is_logged_in(_auth: Auth) -> HttpResponse {
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
||||
|
||||
pub type UserName = String;
|
||||
|
||||
//------------ Auth ----------------------------------------------------------
|
||||
@@ -87,7 +65,7 @@ pub type UserName = String;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Auth {
|
||||
User(UserName),
|
||||
Bearer(Token)
|
||||
Bearer(Token),
|
||||
}
|
||||
|
||||
impl Auth {
|
||||
@@ -101,7 +79,7 @@ impl Auth {
|
||||
let token = Token::from(token.trim());
|
||||
|
||||
if "bearer" == bearer {
|
||||
return Ok(token)
|
||||
return Ok(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +90,7 @@ impl Auth {
|
||||
impl fmt::Display for Auth {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Auth::User(user) => write!(f, "User: {}", user),
|
||||
Auth::User(user) => write!(f, "User: {}", user),
|
||||
Auth::Bearer(token) => write!(f, "Bearer: {}", token),
|
||||
}
|
||||
}
|
||||
@@ -122,7 +100,7 @@ impl Into<Token> for Auth {
|
||||
fn into(self) -> Token {
|
||||
match self {
|
||||
Auth::Bearer(token) => token,
|
||||
_ => Token::from("")
|
||||
_ => Token::from(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,9 +115,8 @@ impl FromRequest for Auth {
|
||||
debug!("Found user: {}", &identity);
|
||||
Ok(Auth::User(identity))
|
||||
} else if let Some(header) = req.headers().get("Authorization") {
|
||||
let token = Auth::extract_bearer_token(
|
||||
header.to_str().map_err(|_| AuthError::InvalidToken)?
|
||||
)?;
|
||||
let token =
|
||||
Auth::extract_bearer_token(header.to_str().map_err(|_| AuthError::InvalidToken)?)?;
|
||||
|
||||
Ok(Auth::Bearer(token))
|
||||
} else {
|
||||
@@ -148,7 +125,6 @@ impl FromRequest for Auth {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ AuthError -----------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -157,7 +133,7 @@ pub enum AuthError {
|
||||
Unauthorised,
|
||||
|
||||
#[display(fmt = "Invalid token")]
|
||||
InvalidToken
|
||||
InvalidToken,
|
||||
}
|
||||
|
||||
impl ResponseError for AuthError {
|
||||
|
||||
+234
-367
File diff suppressed because it is too large
Load Diff
+23
-64
@@ -1,25 +1,17 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use krill_commons::api::{Entitlements, IssuanceRequest};
|
||||
use krill_commons::api::admin::{Handle, ParentCaContact, Token};
|
||||
use krill_commons::api::ca::{RcvdCert, ResourceSet};
|
||||
use krill_commons::api::{Entitlements, IssuanceRequest};
|
||||
use krill_commons::eventsourcing;
|
||||
use krill_commons::remote::id::IdCert;
|
||||
|
||||
use crate::ca::{
|
||||
Evt,
|
||||
Signer,
|
||||
ChildHandle,
|
||||
ParentHandle,
|
||||
ResourceClassName
|
||||
};
|
||||
|
||||
use crate::ca::{ChildHandle, Evt, ParentHandle, ResourceClassName, Signer};
|
||||
|
||||
//------------ Command -----------------------------------------------------
|
||||
|
||||
pub type Cmd<S> = eventsourcing::SentCommand<CmdDet<S>>;
|
||||
|
||||
|
||||
//------------ CommandDetails ----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -27,7 +19,12 @@ pub type Cmd<S> = eventsourcing::SentCommand<CmdDet<S>>;
|
||||
pub enum CmdDet<S: Signer> {
|
||||
// Being a parent
|
||||
AddChild(ChildHandle, Token, Option<IdCert>, ResourceSet),
|
||||
UpdateChild(ChildHandle, Option<Token>, Option<IdCert>, Option<ResourceSet>),
|
||||
UpdateChild(
|
||||
ChildHandle,
|
||||
Option<Token>,
|
||||
Option<IdCert>,
|
||||
Option<ResourceSet>,
|
||||
),
|
||||
CertifyChild(ChildHandle, IssuanceRequest, Token, Arc<RwLock<S>>),
|
||||
|
||||
// Being a child
|
||||
@@ -36,7 +33,7 @@ pub enum CmdDet<S: Signer> {
|
||||
UpdateRcvdCert(ParentHandle, ResourceClassName, RcvdCert, Arc<RwLock<S>>),
|
||||
|
||||
// General
|
||||
Republish(Arc<RwLock<S>>)
|
||||
Republish(Arc<RwLock<S>>),
|
||||
}
|
||||
|
||||
impl<S: Signer> eventsourcing::CommandDetails for CmdDet<S> {
|
||||
@@ -44,7 +41,6 @@ impl<S: Signer> eventsourcing::CommandDetails for CmdDet<S> {
|
||||
}
|
||||
|
||||
impl<S: Signer> CmdDet<S> {
|
||||
|
||||
/// Adds a child to this CA. Will return an error in case you try
|
||||
/// to give the child resources not held by the CA. And until issue
|
||||
/// #25 is implemented, returns an error when the CA is not a TA.
|
||||
@@ -58,33 +54,22 @@ impl<S: Signer> CmdDet<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::AddChild(
|
||||
child_handle,
|
||||
child_token,
|
||||
child_id_cert,
|
||||
child_resources
|
||||
)
|
||||
CmdDet::AddChild(child_handle, child_token, child_id_cert, child_resources),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update_child_resources(
|
||||
handle: &Handle,
|
||||
child_handle: ChildHandle,
|
||||
child_resources: ResourceSet
|
||||
child_resources: ResourceSet,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::UpdateChild(
|
||||
child_handle,
|
||||
None,
|
||||
None,
|
||||
Some(child_resources)
|
||||
)
|
||||
CmdDet::UpdateChild(child_handle, None, None, Some(child_resources)),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/// Certify a child. Will return an error in case the child is
|
||||
/// unknown, or in case resources are not held by the child.
|
||||
pub fn certify_child(
|
||||
@@ -92,42 +77,29 @@ impl<S: Signer> CmdDet<S> {
|
||||
child_handle: Handle,
|
||||
request: IssuanceRequest,
|
||||
token: Token,
|
||||
signer: Arc<RwLock<S>>
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::CertifyChild(child_handle, request, token, signer)
|
||||
CmdDet::CertifyChild(child_handle, request, token, signer),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
pub fn add_parent(
|
||||
handle: &Handle,
|
||||
name: &str,
|
||||
info: ParentCaContact
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::AddParent(Handle::from(name), info)
|
||||
)
|
||||
pub fn add_parent(handle: &Handle, name: &str, info: ParentCaContact) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::AddParent(Handle::from(name), info))
|
||||
}
|
||||
|
||||
pub fn upd_entitlements(
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
entitlements: Entitlements,
|
||||
signer: Arc<RwLock<S>>
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::UpdateEntitlements(
|
||||
parent.clone(),
|
||||
entitlements,
|
||||
signer
|
||||
)
|
||||
CmdDet::UpdateEntitlements(parent.clone(), entitlements, signer),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,29 +108,16 @@ impl<S: Signer> CmdDet<S> {
|
||||
parent: &ParentHandle,
|
||||
class_name: &str,
|
||||
cert: RcvdCert,
|
||||
signer: Arc<RwLock<S>>
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::UpdateRcvdCert(
|
||||
parent.clone(),
|
||||
class_name.to_string(),
|
||||
cert,
|
||||
signer
|
||||
)
|
||||
CmdDet::UpdateRcvdCert(parent.clone(), class_name.to_string(), cert, signer),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
pub fn publish(
|
||||
handle: &Handle,
|
||||
signer: Arc<RwLock<S>>
|
||||
) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(
|
||||
handle,
|
||||
None,
|
||||
CmdDet::Republish(signer)
|
||||
)
|
||||
pub fn publish(handle: &Handle, signer: Arc<RwLock<S>>) -> Cmd<S> {
|
||||
eventsourcing::SentCommand::new(handle, None, CmdDet::Republish(signer))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+16
-13
@@ -1,14 +1,14 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use krill_commons::api::admin::{Handle};
|
||||
use krill_commons::api::ca::{KeyRef};
|
||||
use krill_commons::eventsourcing::{AggregateStoreError};
|
||||
use krill_commons::api::admin::Handle;
|
||||
use krill_commons::api::ca::KeyRef;
|
||||
use krill_commons::eventsourcing::AggregateStoreError;
|
||||
use krill_commons::remote::rfc6492;
|
||||
|
||||
use crate::ca::signing::{Signer};
|
||||
use ca::{KeyStatus};
|
||||
use std::{io, fmt};
|
||||
use crate::ca::signing::Signer;
|
||||
use ca::KeyStatus;
|
||||
use krill_commons::util::httpclient;
|
||||
use std::{fmt, io};
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
@@ -87,13 +87,10 @@ impl Error {
|
||||
pub fn invalid_csr(handle: &Handle, msg: &str) -> Self {
|
||||
Error::InvalidCsr(handle.clone(), msg.to_string())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -136,13 +133,19 @@ impl<S: Signer> ServerError<S> {
|
||||
}
|
||||
|
||||
impl<S: Signer> From<io::Error> for ServerError<S> {
|
||||
fn from(e: io::Error) -> Self { ServerError::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
ServerError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Signer> From<Error> for ServerError<S> {
|
||||
fn from(e: Error) -> Self { ServerError::CertAuth(e) }
|
||||
fn from(e: Error) -> Self {
|
||||
ServerError::CertAuth(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Signer> From<AggregateStoreError> for ServerError<S> {
|
||||
fn from(e: AggregateStoreError) -> Self { ServerError::AggregateStoreError(e) }
|
||||
}
|
||||
fn from(e: AggregateStoreError) -> Self {
|
||||
ServerError::AggregateStoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
+67
-104
@@ -1,34 +1,36 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use rpki::cert::{Cert, TbsCert, KeyUsage, Overclaim};
|
||||
use rpki::crypto::{PublicKeyFormat};
|
||||
use rpki::cert::{Cert, KeyUsage, Overclaim, TbsCert};
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::csr::Csr;
|
||||
use rpki::uri;
|
||||
use rpki::x509::{Serial, Validity, Time};
|
||||
use rpki::x509::{Serial, Time, Validity};
|
||||
|
||||
use krill_commons::api::{IssuanceRequest, RequestResourceLimit, IssuanceResponse};
|
||||
use krill_commons::api::admin::{Handle, ParentCaContact, Token};
|
||||
use krill_commons::api::ca::{CertifiedKey, ChildCa, PublicationDelta, RcvdCert, RepoInfo, ResourceSet, TrustAnchorLocator};
|
||||
use krill_commons::api::ca::{
|
||||
CertifiedKey, ChildCa, PublicationDelta, RcvdCert, RepoInfo, ResourceSet, TrustAnchorLocator,
|
||||
};
|
||||
use krill_commons::api::{IssuanceRequest, IssuanceResponse, RequestResourceLimit};
|
||||
use krill_commons::eventsourcing::StoredEvent;
|
||||
|
||||
use crate::ca::signing::Signer;
|
||||
use ca::{Result, CaType, Error, ParentHandle, ResourceClassName, KeyStatus};
|
||||
use ca::{Rfc8183Id, ResourceClass};
|
||||
|
||||
use ca::{CaType, Error, KeyStatus, ParentHandle, ResourceClassName, Result};
|
||||
use ca::{ResourceClass, Rfc8183Id};
|
||||
|
||||
//------------ Ini -----------------------------------------------------------
|
||||
|
||||
pub type Ini = StoredEvent<IniDet>;
|
||||
|
||||
|
||||
//------------ IniDet --------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct IniDet(Token, Rfc8183Id, RepoInfo, CaType);
|
||||
|
||||
impl IniDet {
|
||||
pub fn token(&self) -> &Token { &self.0 }
|
||||
pub fn token(&self) -> &Token {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (Token, Rfc8183Id, RepoInfo, CaType) {
|
||||
(self.0, self.1, self.2, self.3)
|
||||
@@ -40,15 +42,11 @@ impl IniDet {
|
||||
handle: &Handle,
|
||||
token: Token,
|
||||
info: RepoInfo,
|
||||
signer: Arc<RwLock<S>>
|
||||
signer: Arc<RwLock<S>>,
|
||||
) -> Result<Ini> {
|
||||
let mut signer = signer.write().unwrap();
|
||||
let id = Rfc8183Id::generate(signer.deref_mut())?;
|
||||
Ok(Ini::new(
|
||||
handle,
|
||||
0,
|
||||
IniDet(token, id, info, CaType::Child)
|
||||
))
|
||||
Ok(Ini::new(handle, 0, IniDet(token, id, info, CaType::Child)))
|
||||
}
|
||||
|
||||
pub fn init_ta<S: Signer>(
|
||||
@@ -62,7 +60,8 @@ impl IniDet {
|
||||
|
||||
let id = Rfc8183Id::generate(signer.deref_mut())?;
|
||||
|
||||
let key = signer.create_key(PublicKeyFormat::default())
|
||||
let key = signer
|
||||
.create_key(PublicKeyFormat::default())
|
||||
.map_err(|e| Error::SignerError(e.to_string()))?;
|
||||
|
||||
let token = Token::random(signer.deref());
|
||||
@@ -75,7 +74,7 @@ impl IniDet {
|
||||
Ok(Ini::new(
|
||||
handle,
|
||||
0,
|
||||
IniDet(token, id, info, CaType::Ta(key, tal))
|
||||
IniDet(token, id, info, CaType::Ta(key, tal)),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ impl IniDet {
|
||||
repo_info: &RepoInfo,
|
||||
resources: &ResourceSet,
|
||||
key: &S::KeyId,
|
||||
signer: &S
|
||||
signer: &S,
|
||||
) -> Result<Cert> {
|
||||
let serial: Serial = Serial::random(signer).map_err(Error::signer)?;
|
||||
|
||||
@@ -97,7 +96,7 @@ impl IniDet {
|
||||
Some(name),
|
||||
pub_key.clone(),
|
||||
KeyUsage::Ca,
|
||||
Overclaim::Refuse
|
||||
Overclaim::Refuse,
|
||||
);
|
||||
|
||||
cert.set_basic_ca(Some(true));
|
||||
@@ -110,34 +109,24 @@ impl IniDet {
|
||||
cert.set_v4_resources(Some(resources.v4().deref().clone()));
|
||||
cert.set_v6_resources(Some(resources.v6().deref().clone()));
|
||||
|
||||
cert.into_cert(
|
||||
signer.deref(),
|
||||
key
|
||||
).map_err(Error::signer)
|
||||
cert.into_cert(signer.deref(), key).map_err(Error::signer)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------ Evt ---------------------------------------------------------
|
||||
|
||||
pub type Evt = StoredEvent<EvtDet>;
|
||||
|
||||
|
||||
//------------ CertIssued ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CertIssued {
|
||||
child: Handle,
|
||||
response: IssuanceResponse
|
||||
response: IssuanceResponse,
|
||||
}
|
||||
|
||||
impl CertIssued {
|
||||
pub fn new(
|
||||
child: Handle,
|
||||
response: IssuanceResponse
|
||||
) -> Self {
|
||||
pub fn new(child: Handle, response: IssuanceResponse) -> Self {
|
||||
CertIssued { child, response }
|
||||
}
|
||||
pub fn unwrap(self) -> (Handle, IssuanceResponse) {
|
||||
@@ -145,23 +134,22 @@ impl CertIssued {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CertRequested -----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct CertRequested {
|
||||
parent: ParentHandle,
|
||||
key_status: KeyStatus,
|
||||
request: IssuanceRequest
|
||||
request: IssuanceRequest,
|
||||
}
|
||||
|
||||
impl CertRequested {
|
||||
pub fn new(
|
||||
parent: ParentHandle,
|
||||
key_status: KeyStatus,
|
||||
request: IssuanceRequest
|
||||
) -> Self {
|
||||
CertRequested { parent, key_status, request }
|
||||
pub fn new(parent: ParentHandle, key_status: KeyStatus, request: IssuanceRequest) -> Self {
|
||||
CertRequested {
|
||||
parent,
|
||||
key_status,
|
||||
request,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> (ParentHandle, KeyStatus, IssuanceRequest) {
|
||||
@@ -173,7 +161,9 @@ impl CertRequested {
|
||||
pub fn class_name(&self) -> &str {
|
||||
self.request.class_name()
|
||||
}
|
||||
pub fn status(&self) -> KeyStatus { self.key_status }
|
||||
pub fn status(&self) -> KeyStatus {
|
||||
self.key_status
|
||||
}
|
||||
pub fn limit(&self) -> &RequestResourceLimit {
|
||||
self.request.limit()
|
||||
}
|
||||
@@ -188,7 +178,6 @@ impl Into<IssuanceRequest> for CertRequested {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ CertReceived ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@@ -196,7 +185,7 @@ pub struct CertReceived {
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
key_status: KeyStatus,
|
||||
cert: RcvdCert
|
||||
cert: RcvdCert,
|
||||
}
|
||||
|
||||
impl CertReceived {
|
||||
@@ -204,13 +193,17 @@ impl CertReceived {
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
key_status: KeyStatus,
|
||||
cert: RcvdCert
|
||||
cert: RcvdCert,
|
||||
) -> Self {
|
||||
CertReceived { parent, class_name, key_status, cert }
|
||||
CertReceived {
|
||||
parent,
|
||||
class_name,
|
||||
key_status,
|
||||
cert,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ EvtDet -------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@@ -233,38 +226,32 @@ pub enum EvtDet {
|
||||
|
||||
// Publishing
|
||||
Published(ParentHandle, ResourceClassName, KeyStatus, PublicationDelta),
|
||||
TaPublished(PublicationDelta)
|
||||
TaPublished(PublicationDelta),
|
||||
}
|
||||
|
||||
impl EvtDet {
|
||||
/// This marks a parent as added to the CA.
|
||||
pub (super) fn parent_added(
|
||||
pub(super) fn parent_added(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
parent_handle: ParentHandle,
|
||||
info: ParentCaContact
|
||||
info: ParentCaContact,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::ParentAdded(parent_handle, info)
|
||||
)
|
||||
StoredEvent::new(handle, version, EvtDet::ParentAdded(parent_handle, info))
|
||||
}
|
||||
|
||||
/// This marks a resource class as added under a parent for the CA.
|
||||
pub (super) fn resource_class_added(
|
||||
pub(super) fn resource_class_added(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
parent_handle: ParentHandle,
|
||||
class_name: String,
|
||||
resource_class: ResourceClass
|
||||
resource_class: ResourceClass,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::ResourceClassAdded(
|
||||
parent_handle, class_name, resource_class
|
||||
)
|
||||
EvtDet::ResourceClassAdded(parent_handle, class_name, resource_class),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -274,30 +261,26 @@ impl EvtDet {
|
||||
/// then gets a new certificate, it will send a command to the CA with
|
||||
/// the new certificate to mark it as received, and take other
|
||||
/// appropriate actions (key life cycle, publication).
|
||||
pub (super) fn certificate_requested(
|
||||
pub(super) fn certificate_requested(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
cert_issue_req: CertRequested
|
||||
cert_issue_req: CertRequested,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::CertificateRequested(cert_issue_req)
|
||||
EvtDet::CertificateRequested(cert_issue_req),
|
||||
)
|
||||
}
|
||||
|
||||
/// This marks a certificate as received for the key of the given status
|
||||
/// in a given resource class under a parent.
|
||||
pub (super) fn certificate_received(
|
||||
pub(super) fn certificate_received(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
received: CertReceived
|
||||
received: CertReceived,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::CertificateReceived(received)
|
||||
)
|
||||
StoredEvent::new(handle, version, EvtDet::CertificateReceived(received))
|
||||
}
|
||||
|
||||
/// This marks the pending key as activated. This occurs when a resource
|
||||
@@ -307,70 +290,50 @@ impl EvtDet {
|
||||
/// Note that key roll management is going to be implemented in the near
|
||||
/// future and then there will also be appropriate events for all the
|
||||
/// stages in a key roll.
|
||||
pub (super) fn pending_activated(
|
||||
pub(super) fn pending_activated(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
received: RcvdCert
|
||||
received: RcvdCert,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::PendingKeyActivated(parent, class_name, received)
|
||||
EvtDet::PendingKeyActivated(parent, class_name, received),
|
||||
)
|
||||
}
|
||||
|
||||
/// This marks a delta as published for a key under a resource class
|
||||
/// under a parent CA.
|
||||
pub (super) fn published(
|
||||
pub(super) fn published(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
parent: ParentHandle,
|
||||
class_name: ResourceClassName,
|
||||
key_status: KeyStatus,
|
||||
delta: PublicationDelta
|
||||
delta: PublicationDelta,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::Published(parent, class_name, key_status, delta)
|
||||
EvtDet::Published(parent, class_name, key_status, delta),
|
||||
)
|
||||
}
|
||||
|
||||
pub (super) fn child_added(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
child: ChildCa
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::ChildAdded(child)
|
||||
)
|
||||
pub(super) fn child_added(handle: &Handle, version: u64, child: ChildCa) -> Evt {
|
||||
StoredEvent::new(handle, version, EvtDet::ChildAdded(child))
|
||||
}
|
||||
|
||||
pub (super) fn certificate_issued(
|
||||
pub(super) fn certificate_issued(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
cert_issued: CertIssued
|
||||
cert_issued: CertIssued,
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::CertificateIssued(cert_issued)
|
||||
)
|
||||
StoredEvent::new(handle, version, EvtDet::CertificateIssued(cert_issued))
|
||||
}
|
||||
|
||||
pub (super) fn published_ta(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
delta: PublicationDelta
|
||||
) -> Evt {
|
||||
StoredEvent::new(
|
||||
handle,
|
||||
version,
|
||||
EvtDet::TaPublished(delta)
|
||||
)
|
||||
pub(super) fn published_ta(handle: &Handle, version: u64, delta: PublicationDelta) -> Evt {
|
||||
StoredEvent::new(handle, version, EvtDet::TaPublished(delta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-11
@@ -3,37 +3,36 @@
|
||||
use krill_commons::api::admin::Handle;
|
||||
|
||||
mod certauth;
|
||||
pub use self::certauth::CertAuth;
|
||||
pub use self::certauth::CaType;
|
||||
pub use self::certauth::Rfc8183Id;
|
||||
pub use self::certauth::ResourceClass;
|
||||
pub use self::certauth::CertAuth;
|
||||
pub use self::certauth::KeyStatus;
|
||||
pub use self::certauth::ResourceClass;
|
||||
pub use self::certauth::Rfc8183Id;
|
||||
|
||||
mod commands;
|
||||
pub use self::commands::Cmd;
|
||||
pub use self::commands::CmdDet;
|
||||
|
||||
mod events;
|
||||
pub use self::events::Ini;
|
||||
pub use self::events::IniDet;
|
||||
pub use self::events::CertIssued;
|
||||
pub use self::events::CertReceived;
|
||||
pub use self::events::CertRequested;
|
||||
pub use self::events::Evt;
|
||||
pub use self::events::EvtDet;
|
||||
pub use self::events::CertIssued;
|
||||
pub use self::events::CertRequested;
|
||||
pub use self::events::CertReceived;
|
||||
pub use self::events::Ini;
|
||||
pub use self::events::IniDet;
|
||||
|
||||
mod server;
|
||||
pub use self::server::CaServer;
|
||||
|
||||
mod signing;
|
||||
pub use self::signing::Signer;
|
||||
pub use self::signing::SignSupport;
|
||||
pub use self::signing::Signer;
|
||||
|
||||
mod error;
|
||||
pub use self::error::Error;
|
||||
pub use self::error::ServerError;
|
||||
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type ServerResult<R, S> = std::result::Result<R, ServerError<S>>;
|
||||
pub type ParentHandle = Handle;
|
||||
@@ -44,4 +43,4 @@ pub const TA_NAME: &str = "ta"; // reserved for TA
|
||||
|
||||
pub fn ta_handle() -> Handle {
|
||||
Handle::from(TA_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
+128
-281
@@ -7,78 +7,45 @@ use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
|
||||
use krill_commons::api;
|
||||
use krill_commons::api::{
|
||||
DFLT_CLASS,
|
||||
Entitlements,
|
||||
IssuanceRequest,
|
||||
IssuanceResponse
|
||||
};
|
||||
use krill_commons::api::admin::{
|
||||
AddChildRequest,
|
||||
AddParentRequest,
|
||||
ChildAuthRequest,
|
||||
Handle,
|
||||
ParentCaContact,
|
||||
Token,
|
||||
};
|
||||
use krill_commons::api::ca::{
|
||||
CertAuthList,
|
||||
CertAuthSummary,
|
||||
IssuedCert,
|
||||
RcvdCert,
|
||||
RepoInfo,
|
||||
};
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate,
|
||||
AggregateStore,
|
||||
DiskAggregateStore
|
||||
AddChildRequest, AddParentRequest, ChildAuthRequest, Handle, ParentCaContact, Token,
|
||||
};
|
||||
use krill_commons::api::ca::{CertAuthList, CertAuthSummary, IssuedCert, RcvdCert, RepoInfo};
|
||||
use krill_commons::api::{Entitlements, IssuanceRequest, IssuanceResponse, DFLT_CLASS};
|
||||
use krill_commons::eventsourcing::{Aggregate, AggregateStore, DiskAggregateStore};
|
||||
use krill_commons::remote::builder::SignedMessageBuilder;
|
||||
use krill_commons::remote::{rfc8183, rfc6492};
|
||||
use krill_commons::remote::sigmsg::SignedMessage;
|
||||
use krill_commons::remote::{rfc6492, rfc8183};
|
||||
use krill_commons::util::httpclient;
|
||||
use krill_commons::util::softsigner::SignerKeyId;
|
||||
|
||||
use crate::ca::{
|
||||
self,
|
||||
CmdDet,
|
||||
IniDet,
|
||||
Signer,
|
||||
CertAuth,
|
||||
ParentHandle,
|
||||
ServerResult,
|
||||
ServerError,
|
||||
};
|
||||
use crate::ca::{self, CertAuth, CmdDet, IniDet, ParentHandle, ServerError, ServerResult, Signer};
|
||||
use crate::mq::EventQueueListener;
|
||||
|
||||
|
||||
const CA_NS: &str = "cas";
|
||||
|
||||
|
||||
//------------ CaServer ------------------------------------------------------
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CaServer<S: Signer> {
|
||||
signer: Arc<RwLock<S>>,
|
||||
ca_store: Arc<DiskAggregateStore<CertAuth<S>>>
|
||||
ca_store: Arc<DiskAggregateStore<CertAuth<S>>>,
|
||||
}
|
||||
|
||||
|
||||
impl<S: Signer> CaServer<S> {
|
||||
|
||||
/// Builds a new CaServer. Will return an error if the TA store cannot be
|
||||
/// initialised.
|
||||
pub fn build(
|
||||
work_dir: &PathBuf,
|
||||
events_queue: Arc<EventQueueListener>,
|
||||
signer: S
|
||||
signer: S,
|
||||
) -> ServerResult<Self, S> {
|
||||
let mut ca_store = DiskAggregateStore::<CertAuth<S>>::new(work_dir, CA_NS)?;
|
||||
ca_store.add_listener(events_queue);
|
||||
|
||||
Ok(CaServer {
|
||||
signer: Arc::new(RwLock::new(signer)),
|
||||
ca_store: Arc::new(ca_store)
|
||||
ca_store: Arc::new(ca_store),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,19 +61,13 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
info: RepoInfo,
|
||||
ta_aia: uri::Rsync,
|
||||
ta_uris: Vec<uri::Https>
|
||||
ta_uris: Vec<uri::Https>,
|
||||
) -> ServerResult<(), S> {
|
||||
let handle = ca::ta_handle();
|
||||
if self.ca_store.has(&handle) {
|
||||
Err(ServerError::TrustAnchorInitialisedError)
|
||||
} else {
|
||||
let init = IniDet::init_ta(
|
||||
&handle,
|
||||
info,
|
||||
ta_aia,
|
||||
ta_uris,
|
||||
self.signer.clone()
|
||||
)?;
|
||||
let init = IniDet::init_ta(&handle, info, ta_aia, ta_uris, self.signer.clone())?;
|
||||
|
||||
self.ca_store.add(init)?;
|
||||
|
||||
@@ -125,19 +86,15 @@ impl<S: Signer> CaServer<S> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Republish a CA, this is a no-op when there is nothing to publish.
|
||||
pub fn republish(&self, handle: &Handle) -> ServerResult<(), S> {
|
||||
debug!("Republish CA: {}", handle);
|
||||
let ca = self.ca_store.get_latest(handle)?;
|
||||
|
||||
let cmd = CmdDet::publish(
|
||||
handle,
|
||||
self.signer.clone()
|
||||
);
|
||||
let cmd = CmdDet::publish(handle, self.signer.clone());
|
||||
|
||||
let events = ca.process_command(cmd)?;
|
||||
if ! events.is_empty() {
|
||||
if !events.is_empty() {
|
||||
self.ca_store.update(handle, ca, events)?;
|
||||
}
|
||||
|
||||
@@ -148,7 +105,7 @@ impl<S: Signer> CaServer<S> {
|
||||
pub fn ta_add_child(
|
||||
&self,
|
||||
req: AddChildRequest,
|
||||
service_uri: &uri::Https
|
||||
service_uri: &uri::Https,
|
||||
) -> ServerResult<ParentCaContact, S> {
|
||||
let (handle, resources, auth) = req.unwrap();
|
||||
|
||||
@@ -160,21 +117,16 @@ impl<S: Signer> CaServer<S> {
|
||||
let token = match &auth {
|
||||
ChildAuthRequest::Embedded(token) => token.clone(),
|
||||
ChildAuthRequest::Remote(token) => token.clone(),
|
||||
ChildAuthRequest::Rfc8183(_) => self.random_token()
|
||||
ChildAuthRequest::Rfc8183(_) => self.random_token(),
|
||||
};
|
||||
|
||||
let id_cert = match &auth {
|
||||
ChildAuthRequest::Embedded(_) | ChildAuthRequest::Remote(_) => None,
|
||||
ChildAuthRequest::Rfc8183(req) => Some(req.id_cert().clone())
|
||||
ChildAuthRequest::Rfc8183(req) => Some(req.id_cert().clone()),
|
||||
};
|
||||
|
||||
let add_child = CmdDet::<S>::add_child(
|
||||
&ta_handle,
|
||||
handle.clone(),
|
||||
token,
|
||||
id_cert,
|
||||
resources
|
||||
);
|
||||
let add_child =
|
||||
CmdDet::<S>::add_child(&ta_handle, handle.clone(), token, id_cert, resources);
|
||||
|
||||
let events = ta.process_command(add_child)?;
|
||||
let ta = self.ca_store.update(&ta_handle, ta, events)?;
|
||||
@@ -182,17 +134,10 @@ impl<S: Signer> CaServer<S> {
|
||||
match auth {
|
||||
ChildAuthRequest::Embedded(token) => {
|
||||
Ok(ParentCaContact::for_embedded(ta_handle, token))
|
||||
},
|
||||
ChildAuthRequest::Remote(_token) => {
|
||||
unimplemented!()
|
||||
},
|
||||
}
|
||||
ChildAuthRequest::Remote(_token) => unimplemented!(),
|
||||
ChildAuthRequest::Rfc8183(req) => {
|
||||
|
||||
let service_uri = format!(
|
||||
"{}rfc6492/{}",
|
||||
service_uri.to_string(),
|
||||
ta.handle()
|
||||
);
|
||||
let service_uri = format!("{}rfc6492/{}", service_uri.to_string(), ta.handle());
|
||||
let service_uri = uri::Https::from_string(service_uri).unwrap();
|
||||
let service_uri = rfc8183::ServiceUri::Https(service_uri);
|
||||
|
||||
@@ -201,7 +146,7 @@ impl<S: Signer> CaServer<S> {
|
||||
ta.id_cert().clone(),
|
||||
ta.handle().clone(),
|
||||
handle,
|
||||
service_uri
|
||||
service_uri,
|
||||
);
|
||||
Ok(ParentCaContact::for_rfc6492(response))
|
||||
}
|
||||
@@ -217,20 +162,16 @@ impl<S: Signer> CaServer<S> {
|
||||
/// # CA support
|
||||
///
|
||||
impl<S: Signer> CaServer<S> {
|
||||
|
||||
pub fn get_ca(&self, handle: &Handle) -> ServerResult<Arc<CertAuth<S>>, S> {
|
||||
self.ca_store.get_latest(handle)
|
||||
self.ca_store
|
||||
.get_latest(handle)
|
||||
.map_err(|_| ServerError::UnknownCa(handle.to_string()))
|
||||
}
|
||||
|
||||
/// Verifies an RFC6492 message and returns the child handle, token,
|
||||
/// and content of the request, so that the simple 'list' and 'issue'
|
||||
/// functions can be called.
|
||||
pub fn rfc6492(
|
||||
&self,
|
||||
parent_handle: &Handle,
|
||||
msg: SignedMessage
|
||||
) -> ServerResult<Bytes, S> {
|
||||
pub fn rfc6492(&self, parent_handle: &Handle, msg: SignedMessage) -> ServerResult<Bytes, S> {
|
||||
info!("RFC6492 Request: will check");
|
||||
let (content, token) = {
|
||||
let parent = self.ca_store.get_latest(parent_handle)?;
|
||||
@@ -244,53 +185,35 @@ impl<S: Signer> CaServer<S> {
|
||||
match content {
|
||||
rfc6492::Content::Qry(rfc6492::Qry::Revoke(_)) => {
|
||||
unimplemented!("Revocation not yet supported")
|
||||
},
|
||||
}
|
||||
rfc6492::Content::Qry(rfc6492::Qry::List) => {
|
||||
let entitlements = self.list(
|
||||
parent_handle,
|
||||
&sender_handle,
|
||||
&token
|
||||
)?;
|
||||
let entitlements = self.list(parent_handle, &sender_handle, &token)?;
|
||||
|
||||
let msg = rfc6492::Message::list_response(
|
||||
sender,
|
||||
recipient,
|
||||
entitlements
|
||||
);
|
||||
let msg = rfc6492::Message::list_response(sender, recipient, entitlements);
|
||||
|
||||
self.wrap_rfc6492_response(parent_handle, msg)
|
||||
},
|
||||
}
|
||||
rfc6492::Content::Qry(rfc6492::Qry::Issue(req)) => {
|
||||
let res = self.issue(
|
||||
parent_handle,
|
||||
&sender_handle,
|
||||
req,
|
||||
token
|
||||
)?;
|
||||
let res = self.issue(parent_handle, &sender_handle, req, token)?;
|
||||
|
||||
let msg = rfc6492::Message::issue_response(
|
||||
sender,
|
||||
recipient,
|
||||
res
|
||||
);
|
||||
let msg = rfc6492::Message::issue_response(sender, recipient, res);
|
||||
|
||||
self.wrap_rfc6492_response(parent_handle, msg)
|
||||
},
|
||||
_ => Err(ServerError::custom("Unsupported RFC6492 message"))
|
||||
}
|
||||
_ => Err(ServerError::custom("Unsupported RFC6492 message")),
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_rfc6492_response(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
msg: rfc6492::Message
|
||||
msg: rfc6492::Message,
|
||||
) -> ServerResult<Bytes, S> {
|
||||
debug!("RFC6492 Response wrapping for {}", handle);
|
||||
let ca = self.ca_store.get_latest(handle)?;
|
||||
let res = ca.sign_rfc6492_response(
|
||||
msg,
|
||||
self.signer.read().unwrap().deref()
|
||||
).map_err(ServerError::<S>::CertAuth);
|
||||
let res = ca
|
||||
.sign_rfc6492_response(msg, self.signer.read().unwrap().deref())
|
||||
.map_err(ServerError::<S>::CertAuth);
|
||||
debug!("RFC6492 Response wrapped for {}", handle);
|
||||
res
|
||||
}
|
||||
@@ -300,9 +223,9 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
parent: &Handle,
|
||||
child: &Handle,
|
||||
token: &Token
|
||||
token: &Token,
|
||||
) -> ServerResult<Entitlements, S> {
|
||||
if parent != & ca::ta_handle() {
|
||||
if parent != &ca::ta_handle() {
|
||||
unimplemented!("https://github.com/NLnetLabs/krill/issues/25");
|
||||
} else {
|
||||
let ta = self.get_trust_anchor()?;
|
||||
@@ -320,7 +243,7 @@ impl<S: Signer> CaServer<S> {
|
||||
issue_req: IssuanceRequest,
|
||||
token: Token,
|
||||
) -> ServerResult<IssuanceResponse, S> {
|
||||
if parent != & ca::ta_handle() {
|
||||
if parent != &ca::ta_handle() {
|
||||
unimplemented!("https://github.com/NLnetLabs/krill/issues/25");
|
||||
} else {
|
||||
let ta = self.get_trust_anchor()?;
|
||||
@@ -337,7 +260,7 @@ impl<S: Signer> CaServer<S> {
|
||||
child.clone(),
|
||||
issue_req.clone(),
|
||||
token.clone(),
|
||||
self.signer.clone()
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let events = ta.process_command(cmd)?;
|
||||
@@ -345,12 +268,7 @@ impl<S: Signer> CaServer<S> {
|
||||
|
||||
// New entitlements will include this resource class, and
|
||||
// the newly issued certificate.
|
||||
let response = ta.issuance_response(
|
||||
child,
|
||||
&class_name,
|
||||
&pub_key,
|
||||
&token
|
||||
)?;
|
||||
let response = ta.issuance_response(child, &class_name, &pub_key, &token)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
@@ -359,9 +277,11 @@ impl<S: Signer> CaServer<S> {
|
||||
/// Get the current CAs
|
||||
pub fn cas(&self) -> CertAuthList {
|
||||
CertAuthList::new(
|
||||
self.ca_store.list().into_iter()
|
||||
self.ca_store
|
||||
.list()
|
||||
.into_iter()
|
||||
.map(CertAuthSummary::new)
|
||||
.collect()
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -382,19 +302,11 @@ impl<S: Signer> CaServer<S> {
|
||||
}
|
||||
|
||||
/// Adds a parent to a ca
|
||||
pub fn ca_add_parent(
|
||||
&self,
|
||||
handle: Handle,
|
||||
parent: AddParentRequest
|
||||
) -> ServerResult<(), S> {
|
||||
pub fn ca_add_parent(&self, handle: Handle, parent: AddParentRequest) -> ServerResult<(), S> {
|
||||
let ca = self.get_ca(&handle)?;
|
||||
let (parent_handle, parent_contact) = parent.unwrap();
|
||||
|
||||
let add = CmdDet::add_parent(
|
||||
&handle,
|
||||
parent_handle.as_str(),
|
||||
parent_contact
|
||||
);
|
||||
let add = CmdDet::add_parent(&handle, parent_handle.as_str(), parent_contact);
|
||||
let events = ca.process_command(add)?;
|
||||
|
||||
self.ca_store.update(&handle, ca, events)?;
|
||||
@@ -406,12 +318,12 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
contact: ParentCaContact
|
||||
contact: ParentCaContact,
|
||||
) -> ServerResult<(), S> {
|
||||
let entitlements = self.get_entitlements_from_parent(handle, &contact)?;
|
||||
|
||||
if ! self.update_if_need(handle, parent, entitlements)? {
|
||||
return Ok(()) // Nothing to do
|
||||
if !self.update_if_need(handle, parent, entitlements)? {
|
||||
return Ok(()); // Nothing to do
|
||||
}
|
||||
|
||||
self.send_requests(handle, parent, &contact)
|
||||
@@ -421,25 +333,22 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
contact: &ParentCaContact
|
||||
contact: &ParentCaContact,
|
||||
) -> ServerResult<(), S> {
|
||||
match contact {
|
||||
ParentCaContact::Embedded(_p, token) => {
|
||||
self.send_requests_embedded(handle, parent, token)
|
||||
},
|
||||
ParentCaContact::Rfc6492(res) => {
|
||||
self.send_requests_rfc6492(handle, parent, res)
|
||||
}
|
||||
_ => unimplemented!()
|
||||
ParentCaContact::Rfc6492(res) => self.send_requests_rfc6492(handle, parent, res),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn send_requests_embedded(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent_h: &ParentHandle,
|
||||
token: &Token
|
||||
token: &Token,
|
||||
) -> ServerResult<(), S> {
|
||||
let mut child = self.ca_store.get_latest(handle)?;
|
||||
let requests = child.cert_requests(parent_h);
|
||||
@@ -449,7 +358,7 @@ impl<S: Signer> CaServer<S> {
|
||||
let mut issued_certs: Vec<(String, IssuedCert)> = vec![];
|
||||
|
||||
for req in requests.into_iter() {
|
||||
let (_,_, issuance_req) = req.unwrap();
|
||||
let (_, _, issuance_req) = req.unwrap();
|
||||
|
||||
let class_name = issuance_req.class_name().to_string();
|
||||
let pub_key = issuance_req.csr().public_key().clone();
|
||||
@@ -459,20 +368,15 @@ impl<S: Signer> CaServer<S> {
|
||||
handle.clone(),
|
||||
issuance_req,
|
||||
token.clone(),
|
||||
self.signer.clone()
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let events = parent.process_command(cmd)?;
|
||||
parent = self.ca_store.update(parent_h, parent, events)?;
|
||||
|
||||
let response = parent.issuance_response(
|
||||
handle,
|
||||
&class_name,
|
||||
&pub_key,
|
||||
&token
|
||||
)?;
|
||||
let response = parent.issuance_response(handle, &class_name, &pub_key, &token)?;
|
||||
|
||||
let (_,_,_, issued) = response.unwrap();
|
||||
let (_, _, _, issued) = response.unwrap();
|
||||
|
||||
issued_certs.push((class_name, issued));
|
||||
}
|
||||
@@ -485,7 +389,7 @@ impl<S: Signer> CaServer<S> {
|
||||
parent_h,
|
||||
&class_name,
|
||||
received,
|
||||
self.signer.clone()
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let evts = child.process_command(upd_rcvd_cmd)?;
|
||||
@@ -499,7 +403,7 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent_h: &ParentHandle,
|
||||
parent_res: &rfc8183::ParentResponse
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
) -> ServerResult<(), S> {
|
||||
let mut child = self.ca_store.get_latest(handle)?;
|
||||
let requests = child.cert_requests(parent_h);
|
||||
@@ -507,19 +411,19 @@ impl<S: Signer> CaServer<S> {
|
||||
for req in requests.into_iter() {
|
||||
let sender = parent_res.child_handle().to_string();
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let (_,_, issuance_req) = req.unwrap();
|
||||
let (_, _, issuance_req) = req.unwrap();
|
||||
let issue = rfc6492::Message::issue(sender, recipient, issuance_req);
|
||||
|
||||
let res = self.send_rfc6492_and_validate_response(
|
||||
child.id_key(),
|
||||
parent_res,
|
||||
issue.into_bytes()
|
||||
issue.into_bytes(),
|
||||
)?;
|
||||
|
||||
match res {
|
||||
rfc6492::Res::Error(_) => unimplemented!("Deal with error"),
|
||||
rfc6492::Res::Issue(issue_response) => {
|
||||
let (class_name,_,_, issued) = issue_response.unwrap();
|
||||
let (class_name, _, _, issued) = issue_response.unwrap();
|
||||
let received = RcvdCert::from(issued);
|
||||
|
||||
let update_rcvd_cmd = CmdDet::upd_received_cert(
|
||||
@@ -527,15 +431,13 @@ impl<S: Signer> CaServer<S> {
|
||||
parent_h,
|
||||
&class_name,
|
||||
received,
|
||||
self.signer.clone()
|
||||
self.signer.clone(),
|
||||
);
|
||||
|
||||
let events = child.process_command(update_rcvd_cmd)?;
|
||||
child = self.ca_store.update(handle, child, events)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(ServerError::custom("Got unexpected response to list query"))
|
||||
}
|
||||
_ => return Err(ServerError::custom("Got unexpected response to list query")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,19 +452,15 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
entitlements: Entitlements
|
||||
entitlements: Entitlements,
|
||||
) -> ServerResult<bool, S> {
|
||||
let child = self.ca_store.get_latest(handle)?;
|
||||
|
||||
let update_entitlements_command = CmdDet::upd_entitlements(
|
||||
handle,
|
||||
parent,
|
||||
entitlements,
|
||||
self.signer.clone()
|
||||
);
|
||||
let update_entitlements_command =
|
||||
CmdDet::upd_entitlements(handle, parent, entitlements, self.signer.clone());
|
||||
|
||||
let events = child.process_command(update_entitlements_command)?;
|
||||
if ! events.is_empty() {
|
||||
if !events.is_empty() {
|
||||
self.ca_store.update(handle, child, events)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
@@ -573,16 +471,14 @@ impl<S: Signer> CaServer<S> {
|
||||
fn get_entitlements_from_parent(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
contact: &ParentCaContact
|
||||
contact: &ParentCaContact,
|
||||
) -> ServerResult<api::Entitlements, S> {
|
||||
match contact {
|
||||
ParentCaContact::Embedded(parent, token) => {
|
||||
self.get_entitlements_embedded(handle, parent, token)
|
||||
},
|
||||
ParentCaContact::Rfc6492(res) => {
|
||||
self.get_entitlements_rfc6492(handle, res)
|
||||
}
|
||||
_ => unimplemented!()
|
||||
ParentCaContact::Rfc6492(res) => self.get_entitlements_rfc6492(handle, res),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +486,7 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent: &ParentHandle,
|
||||
token: &Token
|
||||
token: &Token,
|
||||
) -> ServerResult<api::Entitlements, S> {
|
||||
let parent = self.ca_store.get_latest(parent)?;
|
||||
|
||||
@@ -600,7 +496,7 @@ impl<S: Signer> CaServer<S> {
|
||||
fn get_entitlements_rfc6492(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
parent_res: &rfc8183::ParentResponse
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
) -> ServerResult<api::Entitlements, S> {
|
||||
let child = self.ca_store.get_latest(handle)?;
|
||||
|
||||
@@ -609,16 +505,13 @@ impl<S: Signer> CaServer<S> {
|
||||
let recipient = parent_res.parent_handle().to_string();
|
||||
let list = rfc6492::Message::list(sender, recipient);
|
||||
|
||||
let response = self.send_rfc6492_and_validate_response(
|
||||
child.id_key(),
|
||||
parent_res,
|
||||
list.into_bytes()
|
||||
)?;
|
||||
let response =
|
||||
self.send_rfc6492_and_validate_response(child.id_key(), parent_res, list.into_bytes())?;
|
||||
|
||||
match response {
|
||||
rfc6492::Res::Error(_) => unimplemented!("Deal with error response"),
|
||||
rfc6492::Res::List(ent) => Ok(ent),
|
||||
_ => Err(ServerError::custom("Got unexpected response to list query"))
|
||||
_ => Err(ServerError::custom("Got unexpected response to list query")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,44 +519,39 @@ impl<S: Signer> CaServer<S> {
|
||||
&self,
|
||||
signing_key: &SignerKeyId,
|
||||
parent_res: &rfc8183::ParentResponse,
|
||||
msg: Bytes
|
||||
) -> ServerResult<rfc6492::Res, S>{
|
||||
msg: Bytes,
|
||||
) -> ServerResult<rfc6492::Res, S> {
|
||||
// wrap it up and sign it
|
||||
let signed = {
|
||||
SignedMessageBuilder::create(
|
||||
signing_key,
|
||||
self.signer.read().unwrap().deref(),
|
||||
msg
|
||||
)
|
||||
}.map_err(ServerError::custom)?;
|
||||
|
||||
let signed =
|
||||
{ SignedMessageBuilder::create(signing_key, self.signer.read().unwrap().deref(), msg) }
|
||||
.map_err(ServerError::custom)?;
|
||||
|
||||
// send to the server
|
||||
let uri = parent_res.service_uri().to_string();
|
||||
debug!("Sending to parent: {}\n{}",
|
||||
&uri,
|
||||
base64::encode(&signed.as_bytes())
|
||||
debug!(
|
||||
"Sending to parent: {}\n{}",
|
||||
&uri,
|
||||
base64::encode(&signed.as_bytes())
|
||||
);
|
||||
|
||||
let res = httpclient::post_binary(
|
||||
&uri,
|
||||
&signed.as_bytes(),
|
||||
rfc6492::CONTENT_TYPE
|
||||
).map_err(ServerError::HttpClientError)?;
|
||||
let res = httpclient::post_binary(&uri, &signed.as_bytes(), rfc6492::CONTENT_TYPE)
|
||||
.map_err(ServerError::HttpClientError)?;
|
||||
|
||||
// unpack and validate response
|
||||
let msg = match SignedMessage::decode(res.as_ref(), false)
|
||||
.map_err(ServerError::custom) {
|
||||
let msg = match SignedMessage::decode(res.as_ref(), false).map_err(ServerError::custom) {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
error!("Could not parse response: {}", base64::encode(res.as_ref()));
|
||||
return Err(e)
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = msg.validate(parent_res.id_cert()) {
|
||||
error!("Could not validate response: {}", base64::encode(res.as_ref()));
|
||||
return Err(ServerError::custom(e))
|
||||
error!(
|
||||
"Could not validate response: {}",
|
||||
base64::encode(res.as_ref())
|
||||
);
|
||||
return Err(ServerError::custom(e));
|
||||
}
|
||||
|
||||
rfc6492::Message::from_signed_message(&msg)
|
||||
@@ -673,15 +561,6 @@ impl<S: Signer> CaServer<S> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -692,31 +571,14 @@ mod tests {
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use krill_commons::api::{DFLT_CLASS, IssuanceRequest};
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
Token,
|
||||
ParentCaContact
|
||||
};
|
||||
use krill_commons::api::ca::{
|
||||
RepoInfo,
|
||||
ResourceSet,
|
||||
RcvdCert
|
||||
};
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate,
|
||||
AggregateStore,
|
||||
DiskAggregateStore
|
||||
};
|
||||
use ca::EvtDet;
|
||||
use krill_commons::api::admin::{Handle, ParentCaContact, Token};
|
||||
use krill_commons::api::ca::{RcvdCert, RepoInfo, ResourceSet};
|
||||
use krill_commons::api::{IssuanceRequest, DFLT_CLASS};
|
||||
use krill_commons::eventsourcing::{Aggregate, AggregateStore, DiskAggregateStore};
|
||||
use krill_commons::util::softsigner::OpenSslSigner;
|
||||
use krill_commons::util::test;
|
||||
use krill_commons::util::test::{
|
||||
sub_dir,
|
||||
https,
|
||||
rsync,
|
||||
test_under_tmp,
|
||||
};
|
||||
use ca::EvtDet;
|
||||
use krill_commons::util::test::{https, rsync, sub_dir, test_under_tmp};
|
||||
|
||||
fn signer(temp_dir: &PathBuf) -> OpenSslSigner {
|
||||
let signer_dir = sub_dir(temp_dir);
|
||||
@@ -730,11 +592,7 @@ mod tests {
|
||||
|
||||
let event_queue = Arc::new(EventQueueListener::in_mem());
|
||||
|
||||
let server = CaServer::<OpenSslSigner>::build(
|
||||
&d,
|
||||
event_queue,
|
||||
signer
|
||||
).unwrap();
|
||||
let server = CaServer::<OpenSslSigner>::build(&d, event_queue, signer).unwrap();
|
||||
|
||||
let repo_info = {
|
||||
let base_uri = test::rsync("rsync://localhost/repo/ta/");
|
||||
@@ -747,20 +605,18 @@ mod tests {
|
||||
|
||||
assert!(server.get_trust_anchor().is_err());
|
||||
|
||||
server.init_ta(repo_info.clone(), ta_aia, vec![ta_uri]).unwrap();
|
||||
server
|
||||
.init_ta(repo_info.clone(), ta_aia, vec![ta_uri])
|
||||
.unwrap();
|
||||
|
||||
assert!(server.get_trust_anchor().is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn init_ta() {
|
||||
test_under_tmp(|d| {
|
||||
let ca_store = DiskAggregateStore::<CertAuth<OpenSslSigner>>::new(
|
||||
&d, CA_NS
|
||||
).unwrap();
|
||||
let ca_store = DiskAggregateStore::<CertAuth<OpenSslSigner>>::new(&d, CA_NS).unwrap();
|
||||
|
||||
let ta_repo_info = {
|
||||
let base_uri = rsync("rsync://localhost/repo/ta/");
|
||||
@@ -770,7 +626,6 @@ mod tests {
|
||||
|
||||
let ta_handle = ca::ta_handle();
|
||||
|
||||
|
||||
let ta_uri = https("https://localhost/tal/ta.cer");
|
||||
let ta_aia = rsync("rsync://localhost/repo/ta.cer");
|
||||
|
||||
@@ -786,9 +641,9 @@ mod tests {
|
||||
ta_repo_info,
|
||||
ta_aia,
|
||||
vec![ta_uri],
|
||||
|
||||
signer.clone()
|
||||
).unwrap();
|
||||
signer.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
ca_store.add(ta_ini).unwrap();
|
||||
let ta = ca_store.get_latest(&ta_handle).unwrap();
|
||||
@@ -813,8 +668,9 @@ mod tests {
|
||||
&child_handle,
|
||||
child_token.clone(),
|
||||
ca_repo_info,
|
||||
signer.clone()
|
||||
).unwrap();
|
||||
signer.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
ca_store.add(ca_ini).unwrap();
|
||||
let child = ca_store.get_latest(&child_handle).unwrap();
|
||||
@@ -831,7 +687,7 @@ mod tests {
|
||||
child_handle.clone(),
|
||||
child_token.clone(),
|
||||
None,
|
||||
child_rs
|
||||
child_rs,
|
||||
);
|
||||
|
||||
let events = ta.process_command(cmd).unwrap();
|
||||
@@ -844,16 +700,9 @@ mod tests {
|
||||
// - Parent added
|
||||
//
|
||||
|
||||
let parent = ParentCaContact::for_embedded(
|
||||
ta_handle.clone(),
|
||||
child_token.clone()
|
||||
);
|
||||
let parent = ParentCaContact::for_embedded(ta_handle.clone(), child_token.clone());
|
||||
|
||||
let add_parent = CmdDet::add_parent(
|
||||
&child_handle,
|
||||
ta_handle.as_str(),
|
||||
parent
|
||||
);
|
||||
let add_parent = CmdDet::add_parent(&child_handle, ta_handle.as_str(), parent);
|
||||
|
||||
let events = child.process_command(add_parent).unwrap();
|
||||
let child = ca_store.update(&child_handle, child, events).unwrap();
|
||||
@@ -869,12 +718,8 @@ mod tests {
|
||||
|
||||
let entitlements = ta.list(&child_handle, &child_token).unwrap();
|
||||
|
||||
let upd_ent = CmdDet::upd_entitlements(
|
||||
&child_handle,
|
||||
&ta_handle,
|
||||
entitlements,
|
||||
signer.clone()
|
||||
);
|
||||
let upd_ent =
|
||||
CmdDet::upd_entitlements(&child_handle, &ta_handle, entitlements, signer.clone());
|
||||
|
||||
let events = child.process_command(upd_ent).unwrap();
|
||||
assert_eq!(2, events.len()); // rc and csr
|
||||
@@ -883,7 +728,7 @@ mod tests {
|
||||
|
||||
let req = match req_evt {
|
||||
EvtDet::CertificateRequested(req) => req,
|
||||
_ => panic!("Expected Csr")
|
||||
_ => panic!("Expected Csr"),
|
||||
};
|
||||
|
||||
let (_handle, _key_status, issuance_req) = req.unwrap();
|
||||
@@ -899,16 +744,14 @@ mod tests {
|
||||
// - Publication
|
||||
//
|
||||
|
||||
let request = IssuanceRequest::new(
|
||||
DFLT_CLASS.to_string(), limit, csr
|
||||
);
|
||||
let request = IssuanceRequest::new(DFLT_CLASS.to_string(), limit, csr);
|
||||
|
||||
let ta_cmd = CmdDet::certify_child(
|
||||
&ta_handle,
|
||||
child_handle.clone(),
|
||||
request,
|
||||
child_token.clone(),
|
||||
signer.clone()
|
||||
signer.clone(),
|
||||
);
|
||||
|
||||
let ta_events = ta.process_command(ta_cmd).unwrap();
|
||||
@@ -917,7 +760,7 @@ mod tests {
|
||||
|
||||
let issued = match issued_evt {
|
||||
EvtDet::CertificateIssued(issued) => issued,
|
||||
_ => panic!("Expected issued certificate.")
|
||||
_ => panic!("Expected issued certificate."),
|
||||
};
|
||||
|
||||
let (handle, issuance_res) = issued.unwrap();
|
||||
@@ -937,11 +780,15 @@ mod tests {
|
||||
let rcvd_cert = RcvdCert::from(issued);
|
||||
|
||||
let upd_rcvd = CmdDet::upd_received_cert(
|
||||
&child_handle, &ta_handle, DFLT_CLASS, rcvd_cert, signer.clone()
|
||||
&child_handle,
|
||||
&ta_handle,
|
||||
DFLT_CLASS,
|
||||
rcvd_cert,
|
||||
signer.clone(),
|
||||
);
|
||||
|
||||
let events = child.process_command(upd_rcvd).unwrap();
|
||||
let _child = ca_store.update(&child_handle, child, events).unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-48
@@ -8,32 +8,37 @@ use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
|
||||
use rpki::crl::{Crl, TbsCertList};
|
||||
use rpki::crypto::{self, DigestAlgorithm, KeyIdentifier, SigningError};
|
||||
use rpki::crypto::signer::KeyError;
|
||||
use rpki::manifest::{Manifest, ManifestContent, FileAndHash};
|
||||
use rpki::crypto::{self, DigestAlgorithm, KeyIdentifier, SigningError};
|
||||
use rpki::manifest::{FileAndHash, Manifest, ManifestContent};
|
||||
use rpki::sigobj::SignedObjectBuilder;
|
||||
use rpki::x509::{Serial, Time, Validity};
|
||||
|
||||
use krill_commons::api::ca::{
|
||||
AddedObject,
|
||||
CertifiedKey,
|
||||
CurrentObject,
|
||||
ObjectsDelta,
|
||||
PublicationDelta,
|
||||
RepoInfo,
|
||||
Revocation,
|
||||
RevocationsDelta,
|
||||
UpdatedObject,
|
||||
AddedObject, CertifiedKey, CurrentObject, ObjectsDelta, PublicationDelta, RepoInfo, Revocation,
|
||||
RevocationsDelta, UpdatedObject,
|
||||
};
|
||||
|
||||
use krill_commons::util::softsigner::SignerKeyId;
|
||||
|
||||
|
||||
//------------ Signer --------------------------------------------------------
|
||||
|
||||
pub trait Signer: crypto::Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send +'static {}
|
||||
impl<T: crypto::Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send + 'static > Signer for T {}
|
||||
|
||||
pub trait Signer:
|
||||
crypto::Signer<KeyId = SignerKeyId> + Clone + Debug + Serialize + Sized + Sync + Send + 'static
|
||||
{
|
||||
}
|
||||
impl<
|
||||
T: crypto::Signer<KeyId = SignerKeyId>
|
||||
+ Clone
|
||||
+ Debug
|
||||
+ Serialize
|
||||
+ Sized
|
||||
+ Sync
|
||||
+ Send
|
||||
+ 'static,
|
||||
> Signer for T
|
||||
{
|
||||
}
|
||||
|
||||
//------------ CaSignSupport -------------------------------------------------
|
||||
|
||||
@@ -41,7 +46,6 @@ impl<T: crypto::Signer<KeyId=SignerKeyId> + Clone + Debug + Serialize + Sized +
|
||||
pub struct SignSupport;
|
||||
|
||||
impl SignSupport {
|
||||
|
||||
/// Publish for the given Key and repository.
|
||||
///
|
||||
/// Any updates for existing objects will result in Update, rather
|
||||
@@ -52,13 +56,14 @@ impl SignSupport {
|
||||
ca_key: &CertifiedKey,
|
||||
repo_info: &RepoInfo,
|
||||
name_space: &str,
|
||||
mut objects_delta: ObjectsDelta
|
||||
mut objects_delta: ObjectsDelta,
|
||||
) -> Result<PublicationDelta, SignError<S>> {
|
||||
|
||||
let aia = ca_key.incoming_cert().uri();
|
||||
let key_id = ca_key.key_id();
|
||||
|
||||
let pub_key = signer.read().unwrap()
|
||||
let pub_key = signer
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_key_info(key_id)
|
||||
.map_err(SignError::KeyError)?;
|
||||
|
||||
@@ -105,20 +110,18 @@ impl SignSupport {
|
||||
tomorrow,
|
||||
revocations.to_crl_entries(),
|
||||
aki,
|
||||
serial_number
|
||||
serial_number,
|
||||
);
|
||||
|
||||
crl.into_crl(
|
||||
signer.read().unwrap().deref(),
|
||||
key_id
|
||||
).map_err(SignError::SigningError)?
|
||||
crl.into_crl(signer.read().unwrap().deref(), key_id)
|
||||
.map_err(SignError::SigningError)?
|
||||
};
|
||||
|
||||
match current_objects.insert(crl_name.clone(), CurrentObject::from(&crl)) {
|
||||
None => {
|
||||
let added = AddedObject::new(crl_name, CurrentObject::from(&crl));
|
||||
objects_delta.add(added);
|
||||
},
|
||||
}
|
||||
Some(old_crl) => {
|
||||
let hash = old_crl.content().to_encoded_hash();
|
||||
let updated = UpdatedObject::new(crl_name, CurrentObject::from(&crl), hash);
|
||||
@@ -132,29 +135,30 @@ impl SignSupport {
|
||||
now,
|
||||
tomorrow,
|
||||
DigestAlgorithm::default(),
|
||||
current_objects.mft_entries().iter()
|
||||
current_objects.mft_entries().iter(),
|
||||
);
|
||||
|
||||
mft_content.into_manifest(
|
||||
SignedObjectBuilder::new(
|
||||
Serial::random(
|
||||
signer.read().unwrap().deref()
|
||||
).map_err(SignError::SignerError)?,
|
||||
Validity::new(now, next_week),
|
||||
crl_uri,
|
||||
aia.clone(),
|
||||
mft_uri.clone()
|
||||
),
|
||||
signer.read().unwrap().deref(),
|
||||
key_id,
|
||||
).map_err(SignError::SigningError)?
|
||||
mft_content
|
||||
.into_manifest(
|
||||
SignedObjectBuilder::new(
|
||||
Serial::random(signer.read().unwrap().deref())
|
||||
.map_err(SignError::SignerError)?,
|
||||
Validity::new(now, next_week),
|
||||
crl_uri,
|
||||
aia.clone(),
|
||||
mft_uri.clone(),
|
||||
),
|
||||
signer.read().unwrap().deref(),
|
||||
key_id,
|
||||
)
|
||||
.map_err(SignError::SigningError)?
|
||||
};
|
||||
|
||||
match old_mft {
|
||||
None => {
|
||||
let added = AddedObject::new(mft_name, CurrentObject::from(&mft));
|
||||
objects_delta.add(added);
|
||||
},
|
||||
}
|
||||
Some(old_mft) => {
|
||||
let hash = old_mft.content().to_encoded_hash();
|
||||
let updated = UpdatedObject::new(mft_name, CurrentObject::from(&mft), hash);
|
||||
@@ -167,7 +171,7 @@ impl SignSupport {
|
||||
tomorrow,
|
||||
number,
|
||||
revocations_delta,
|
||||
objects_delta
|
||||
objects_delta,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -176,15 +180,13 @@ trait ManifestEntry {
|
||||
fn mft_bytes(&self) -> Bytes;
|
||||
fn mft_hash(&self) -> Bytes {
|
||||
Bytes::from(
|
||||
DigestAlgorithm::default().digest(
|
||||
self.mft_bytes().as_ref()).as_ref()
|
||||
DigestAlgorithm::default()
|
||||
.digest(self.mft_bytes().as_ref())
|
||||
.as_ref(),
|
||||
)
|
||||
}
|
||||
fn mft_entry(&self, name: &str) -> FileAndHash<Bytes, Bytes> {
|
||||
FileAndHash::new(
|
||||
Bytes::from(name),
|
||||
self.mft_hash()
|
||||
)
|
||||
FileAndHash::new(Bytes::from(name), self.mft_hash())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +196,6 @@ impl ManifestEntry for Crl {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ SignError -----------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
|
||||
+106
-107
@@ -1,19 +1,19 @@
|
||||
use crate::http::ssl;
|
||||
use clap::{App, Arg};
|
||||
use krill_commons::api::admin::Token;
|
||||
use krill_commons::util::ext_serde;
|
||||
use log::LevelFilter;
|
||||
use rpki::uri;
|
||||
use serde::de;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Read;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use clap::{App, Arg};
|
||||
use log::LevelFilter;
|
||||
use rpki::uri;
|
||||
use syslog::Facility;
|
||||
use serde::de;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use toml;
|
||||
use krill_commons::util::ext_serde;
|
||||
use crate::http::ssl;
|
||||
use krill_commons::api::admin::Token;
|
||||
|
||||
const SERVER_NAME: &str = "Krill";
|
||||
|
||||
@@ -22,20 +22,36 @@ const SERVER_NAME: &str = "Krill";
|
||||
pub struct ConfigDefaults;
|
||||
|
||||
impl ConfigDefaults {
|
||||
fn ip() -> IpAddr { IpAddr::V4(Ipv4Addr::new(127,0,0,1))}
|
||||
fn port() -> u16 { 3000 }
|
||||
fn use_ssl() -> SslChoice { SslChoice::Test }
|
||||
fn data_dir() -> PathBuf { PathBuf::from("./data")}
|
||||
fn ip() -> IpAddr {
|
||||
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
|
||||
}
|
||||
fn port() -> u16 {
|
||||
3000
|
||||
}
|
||||
fn use_ssl() -> SslChoice {
|
||||
SslChoice::Test
|
||||
}
|
||||
fn data_dir() -> PathBuf {
|
||||
PathBuf::from("./data")
|
||||
}
|
||||
fn rsync_base() -> uri::Rsync {
|
||||
uri::Rsync::from_str("rsync://localhost/repo/").unwrap()
|
||||
}
|
||||
fn rrdp_base_uri() -> uri::Https {
|
||||
uri::Https::from_str("https://localhost:3000/rrdp/").unwrap()
|
||||
}
|
||||
fn log_level() -> LevelFilter { LevelFilter::Info }
|
||||
fn log_type() -> LogType { LogType::Stderr }
|
||||
fn syslog_facility() -> Facility { Facility::LOG_DAEMON }
|
||||
fn log_file() -> PathBuf { PathBuf::from("./krill.log")}
|
||||
fn log_level() -> LevelFilter {
|
||||
LevelFilter::Info
|
||||
}
|
||||
fn log_type() -> LogType {
|
||||
LogType::Stderr
|
||||
}
|
||||
fn syslog_facility() -> Facility {
|
||||
Facility::LOG_DAEMON
|
||||
}
|
||||
fn log_file() -> PathBuf {
|
||||
PathBuf::from("./krill.log")
|
||||
}
|
||||
fn auth_token() -> Token {
|
||||
use std::env;
|
||||
|
||||
@@ -45,12 +61,10 @@ impl ConfigDefaults {
|
||||
eprintln!("You MUST provide a value for the master API key, either by setting \"auth_token\" in the config file, or by setting the KRILL_AUTH_TOKEN environment variable.");
|
||||
::std::process::exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Config --------------------------------------------------------
|
||||
|
||||
/// Global configuration for the Krill Server.
|
||||
@@ -60,27 +74,22 @@ impl ConfigDefaults {
|
||||
/// to override any of the settings in the config file.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
|
||||
#[serde(default="ConfigDefaults::ip")]
|
||||
#[serde(default = "ConfigDefaults::ip")]
|
||||
ip: IpAddr,
|
||||
|
||||
#[serde(default="ConfigDefaults::port")]
|
||||
#[serde(default = "ConfigDefaults::port")]
|
||||
port: u16,
|
||||
|
||||
#[serde(default="ConfigDefaults::use_ssl")]
|
||||
#[serde(default = "ConfigDefaults::use_ssl")]
|
||||
use_ssl: SslChoice,
|
||||
|
||||
#[serde(default="ConfigDefaults::data_dir")]
|
||||
#[serde(default = "ConfigDefaults::data_dir")]
|
||||
pub data_dir: PathBuf,
|
||||
|
||||
#[serde(
|
||||
default = "ConfigDefaults::rsync_base",
|
||||
)]
|
||||
#[serde(default = "ConfigDefaults::rsync_base")]
|
||||
pub rsync_base: uri::Rsync,
|
||||
|
||||
#[serde(
|
||||
default = "ConfigDefaults::rrdp_base_uri",
|
||||
)]
|
||||
#[serde(default = "ConfigDefaults::rrdp_base_uri")]
|
||||
pub rrdp_base_uri: uri::Https,
|
||||
|
||||
#[serde(
|
||||
@@ -102,7 +111,7 @@ pub struct Config {
|
||||
log_file: PathBuf,
|
||||
|
||||
#[serde(default = "ConfigDefaults::auth_token")]
|
||||
pub auth_token: Token
|
||||
pub auth_token: Token,
|
||||
}
|
||||
|
||||
/// # Accessors
|
||||
@@ -151,16 +160,14 @@ impl Config {
|
||||
|
||||
/// # Create
|
||||
impl Config {
|
||||
pub fn test(
|
||||
data_dir: &PathBuf,
|
||||
) -> Self {
|
||||
pub fn test(data_dir: &PathBuf) -> Self {
|
||||
let ip = ConfigDefaults::ip();
|
||||
let port = ConfigDefaults::port();
|
||||
let use_ssl = SslChoice::Test;
|
||||
let data_dir = data_dir.clone();
|
||||
let rsync_base = ConfigDefaults::rsync_base();
|
||||
let rrdp_base_uri = ConfigDefaults::rrdp_base_uri();
|
||||
let log_level = LevelFilter::Info;
|
||||
let log_level = LevelFilter::Info;
|
||||
let log_type = LogType::Stderr;
|
||||
let mut log_file = data_dir.clone();
|
||||
log_file.push("krill.log");
|
||||
@@ -178,7 +185,7 @@ impl Config {
|
||||
log_type,
|
||||
log_file,
|
||||
syslog_facility,
|
||||
auth_token
|
||||
auth_token,
|
||||
};
|
||||
c.init_logging().unwrap();
|
||||
c
|
||||
@@ -188,19 +195,24 @@ impl Config {
|
||||
pub fn create() -> Result<Self, ConfigError> {
|
||||
let matches = App::new("NLnet Labs RRDP Server")
|
||||
.version("0.1b")
|
||||
.arg(Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.value_name("FILE")
|
||||
.help("Specify non-default config file. If no file is \
|
||||
specified './daemon/defaults/krill.conf' will be used to \
|
||||
determine default values for all settings. Note that you \
|
||||
can use any of the following options to override any of \
|
||||
these values..")
|
||||
.required(false))
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.value_name("FILE")
|
||||
.help(
|
||||
"Specify non-default config file. If no file is \
|
||||
specified './daemon/defaults/krill.conf' will be used to \
|
||||
determine default values for all settings. Note that you \
|
||||
can use any of the following options to override any of \
|
||||
these values..",
|
||||
)
|
||||
.required(false),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let config_file = matches.value_of("config")
|
||||
let config_file = matches
|
||||
.value_of("config")
|
||||
.unwrap_or("./daemon/defaults/krill.conf");
|
||||
|
||||
let c = Self::read_config(config_file)?;
|
||||
@@ -216,7 +228,7 @@ impl Config {
|
||||
let c: Config = toml::from_slice(v.as_slice())?;
|
||||
|
||||
if c.port < 1024 {
|
||||
return Err(ConfigError::other("Port number must be >1024"))
|
||||
return Err(ConfigError::other("Port number must be >1024"));
|
||||
}
|
||||
|
||||
Ok(c)
|
||||
@@ -232,53 +244,39 @@ impl Config {
|
||||
dispatch = {
|
||||
if self.log_level == LevelFilter::Debug {
|
||||
dispatch.format(|out, message, record| {
|
||||
out.finish(
|
||||
format_args!(
|
||||
"{} [{}] [{}] {}",
|
||||
chrono::Local::now()
|
||||
.format("%Y-%m-%d %H:%M:%S"),
|
||||
record.target(),
|
||||
record.level(),
|
||||
message
|
||||
)
|
||||
)
|
||||
out.finish(format_args!(
|
||||
"{} [{}] [{}] {}",
|
||||
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||
record.target(),
|
||||
record.level(),
|
||||
message
|
||||
))
|
||||
})
|
||||
} else {
|
||||
dispatch.format(|out, message, record| {
|
||||
out.finish(
|
||||
format_args!(
|
||||
"{} [{}] {}",
|
||||
chrono::Local::now()
|
||||
.format("%Y-%m-%d %H:%M:%S"),
|
||||
record.level(),
|
||||
message
|
||||
)
|
||||
)
|
||||
out.finish(format_args!(
|
||||
"{} [{}] {}",
|
||||
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||
record.level(),
|
||||
message
|
||||
))
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
dispatch.level(self.log_level)
|
||||
dispatch
|
||||
.level(self.log_level)
|
||||
.chain(file)
|
||||
.apply()
|
||||
.map_err(|e| {
|
||||
ConfigError::Other(
|
||||
format!("Failed to init file logging: {}", e)
|
||||
)
|
||||
ConfigError::Other(format!("Failed to init file logging: {}", e))
|
||||
})?;
|
||||
},
|
||||
}
|
||||
|
||||
LogType::Syslog => {
|
||||
syslog::init(
|
||||
self.syslog_facility,
|
||||
self.log_level,
|
||||
Some(SERVER_NAME)
|
||||
).map_err(|e| {
|
||||
ConfigError::Other(
|
||||
format!("Failed to init syslog: {}", e)
|
||||
)
|
||||
})?;
|
||||
},
|
||||
syslog::init(self.syslog_facility, self.log_level, Some(SERVER_NAME))
|
||||
.map_err(|e| ConfigError::Other(format!("Failed to init syslog: {}", e)))?;
|
||||
}
|
||||
|
||||
LogType::Stderr => {
|
||||
let dispatch = fern::Dispatch::new()
|
||||
@@ -286,9 +284,7 @@ impl Config {
|
||||
.chain(io::stderr());
|
||||
|
||||
dispatch.apply().map_err(|e| {
|
||||
ConfigError::Other(
|
||||
format!("Failed to init stderr logging: {}", e)
|
||||
)
|
||||
ConfigError::Other(format!("Failed to init stderr logging: {}", e))
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@@ -299,17 +295,17 @@ impl Config {
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum ConfigError {
|
||||
#[display(fmt ="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt ="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
TomlError(toml::de::Error),
|
||||
|
||||
#[display(fmt ="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
RpkiUriError(uri::Error),
|
||||
|
||||
#[display(fmt ="{}", _0)]
|
||||
Other(String)
|
||||
#[display(fmt = "{}", _0)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl ConfigError {
|
||||
@@ -336,7 +332,6 @@ impl From<uri::Error> for ConfigError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ LogType -------------------------------------------------------
|
||||
|
||||
/// The target to log to.
|
||||
@@ -344,10 +339,9 @@ impl From<uri::Error> for ConfigError {
|
||||
pub enum LogType {
|
||||
Syslog,
|
||||
Stderr,
|
||||
File
|
||||
File,
|
||||
}
|
||||
|
||||
|
||||
//--- PartialEq and Eq
|
||||
|
||||
impl PartialEq for LogType {
|
||||
@@ -356,25 +350,28 @@ impl PartialEq for LogType {
|
||||
(&LogType::Syslog, &LogType::Syslog) => true,
|
||||
(&LogType::Stderr, &LogType::Stderr) => true,
|
||||
(&LogType::File, &LogType::File) => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for LogType { }
|
||||
impl Eq for LogType {}
|
||||
|
||||
impl<'de> Deserialize<'de> for LogType {
|
||||
fn deserialize<D>(d: D) -> Result<LogType, D::Error>
|
||||
where D: Deserializer<'de> {
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(d)?;
|
||||
match string.as_str() {
|
||||
"stderr" => Ok(LogType::Stderr),
|
||||
"syslog" => Ok(LogType::Syslog),
|
||||
"file" => Ok(LogType::File),
|
||||
_ => Err(
|
||||
de::Error::custom(
|
||||
format!("expected \"stderr\", \"syslog\", or \
|
||||
\"file\", found : \"{}\"", string)))
|
||||
_ => Err(de::Error::custom(format!(
|
||||
"expected \"stderr\", \"syslog\", or \
|
||||
\"file\", found : \"{}\"",
|
||||
string
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,25 +379,27 @@ impl<'de> Deserialize<'de> for LogType {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SslChoice {
|
||||
Yes,
|
||||
Test
|
||||
Test,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SslChoice {
|
||||
fn deserialize<D>(d: D) -> Result<SslChoice, D::Error>
|
||||
where D: Deserializer<'de> {
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(d)?;
|
||||
match string.as_str() {
|
||||
"yes" => Ok(SslChoice::Yes),
|
||||
"yes" => Ok(SslChoice::Yes),
|
||||
"test" => Ok(SslChoice::Test),
|
||||
_ => Err(
|
||||
de::Error::custom(
|
||||
format!("expected \"yes\", or \"test\", \
|
||||
found: \"{}\"", string)))
|
||||
_ => Err(de::Error::custom(format!(
|
||||
"expected \"yes\", or \"test\", \
|
||||
found: \"{}\"",
|
||||
string
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+124
-193
@@ -1,24 +1,17 @@
|
||||
//! Process requests received, delegate, and wrap up the responses.
|
||||
use actix_web::{
|
||||
HttpResponse,
|
||||
ResponseError
|
||||
};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{
|
||||
self,
|
||||
Json,
|
||||
Path,
|
||||
};
|
||||
use actix_web::web::{self, Json, Path};
|
||||
use actix_web::{HttpResponse, ResponseError};
|
||||
use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
|
||||
use krill_commons::api::{admin, publication, ErrorCode, ErrorResponse, IssuanceRequest};
|
||||
use krill_commons::api::admin::{Handle, CertAuthInit, AddChildRequest, AddParentRequest};
|
||||
use krill_commons::api::admin::{AddChildRequest, AddParentRequest, CertAuthInit, Handle};
|
||||
use krill_commons::api::rrdp::VerificationError;
|
||||
use krill_commons::util::softsigner::OpenSslSigner;
|
||||
use krill_commons::api::{admin, publication, ErrorCode, ErrorResponse, IssuanceRequest};
|
||||
use krill_commons::remote::api::ClientInfo;
|
||||
use krill_commons::remote::sigmsg::SignedMessage;
|
||||
use krill_commons::remote::rfc6492;
|
||||
use krill_commons::remote::sigmsg::SignedMessage;
|
||||
use krill_commons::util::softsigner::OpenSslSigner;
|
||||
use krill_pubd::publishers::PublisherError;
|
||||
use krill_pubd::repo::RrdpServerError;
|
||||
|
||||
@@ -36,13 +29,11 @@ const NOT_FOUND: &[u8] = include_bytes!("../ui/dist/404.html");
|
||||
///
|
||||
/// XXX TODO: Use actix Json<> when returning values
|
||||
fn render_json<O: Serialize>(object: O) -> HttpResponse {
|
||||
match serde_json::to_string(&object){
|
||||
Ok(enc) => {
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(enc)
|
||||
},
|
||||
Err(e) => server_error(&Error::JsonError(e))
|
||||
match serde_json::to_string(&object) {
|
||||
Ok(enc) => HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(enc),
|
||||
Err(e) => server_error(&Error::JsonError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +44,13 @@ fn server_error(error: &Error) -> HttpResponse {
|
||||
error.error_response()
|
||||
}
|
||||
|
||||
|
||||
fn render_empty_res(res: Result<(), krillserver::Error>) -> HttpResponse {
|
||||
match res {
|
||||
Ok(()) => api_ok(),
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A clean 404 result for the API (no content, not for humans)
|
||||
fn api_not_found() -> HttpResponse {
|
||||
HttpResponse::build(StatusCode::NOT_FOUND).finish()
|
||||
@@ -87,15 +76,20 @@ pub fn api_health(_auth: Auth) -> HttpResponse {
|
||||
}
|
||||
|
||||
fn if_allowed<F>(allowed: bool, op: F) -> HttpResponse
|
||||
where F: FnOnce() -> HttpResponse {
|
||||
if allowed { op() } else { HttpResponse::Forbidden().finish() }
|
||||
where
|
||||
F: FnOnce() -> HttpResponse,
|
||||
{
|
||||
if allowed {
|
||||
op()
|
||||
} else {
|
||||
HttpResponse::Forbidden().finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn if_api_allowed<F>(
|
||||
server: &web::Data<AppServer>,
|
||||
auth: &Auth,
|
||||
op: F
|
||||
) -> HttpResponse where F: FnOnce() -> HttpResponse {
|
||||
fn if_api_allowed<F>(server: &web::Data<AppServer>, auth: &Auth, op: F) -> HttpResponse
|
||||
where
|
||||
F: FnOnce() -> HttpResponse,
|
||||
{
|
||||
let allowed = server.read().is_api_allowed(auth);
|
||||
if_allowed(allowed, op)
|
||||
}
|
||||
@@ -104,8 +98,11 @@ fn if_publication_allowed<F>(
|
||||
server: &web::Data<AppServer>,
|
||||
handle: &Handle,
|
||||
auth: &Auth,
|
||||
op: F
|
||||
) -> HttpResponse where F: FnOnce() -> HttpResponse {
|
||||
op: F,
|
||||
) -> HttpResponse
|
||||
where
|
||||
F: FnOnce() -> HttpResponse,
|
||||
{
|
||||
let allowed = server.read().is_publication_api_allowed(handle, auth);
|
||||
if_allowed(allowed, op)
|
||||
}
|
||||
@@ -113,14 +110,14 @@ fn if_publication_allowed<F>(
|
||||
//------------ Admin: Publishers ---------------------------------------------
|
||||
|
||||
/// Returns a json structure with all publishers in it.
|
||||
pub fn publishers(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
pub fn publishers(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
let publishers = server.read().publishers();
|
||||
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_json(admin::PublisherList::build(&publishers, "/api/v1/publishers"))
|
||||
render_json(admin::PublisherList::build(
|
||||
&publishers,
|
||||
"/api/v1/publishers",
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,9 +126,9 @@ pub fn publishers(
|
||||
pub fn add_publisher(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
pbl: Json<admin::PublisherRequest>
|
||||
pbl: Json<admin::PublisherRequest>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, ||{
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.write().add_publisher(pbl.into_inner()))
|
||||
})
|
||||
}
|
||||
@@ -142,7 +139,7 @@ pub fn add_publisher(
|
||||
pub fn deactivate_publisher(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.write().deactivate_publisher(&handle))
|
||||
@@ -154,22 +151,15 @@ pub fn deactivate_publisher(
|
||||
pub fn publisher_details(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, ||{
|
||||
match server.read().publisher(&handle) {
|
||||
Ok(None) => api_not_found(),
|
||||
Ok(Some(publisher)) => {
|
||||
render_json(
|
||||
&publisher.as_api_details()
|
||||
)
|
||||
},
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
}
|
||||
if_api_allowed(&server, &auth, || match server.read().publisher(&handle) {
|
||||
Ok(None) => api_not_found(),
|
||||
Ok(Some(publisher)) => render_json(&publisher.as_api_details()),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//------------ Publication ---------------------------------------------------
|
||||
|
||||
/// Processes an RFC8181 query and returns the appropriate response.
|
||||
@@ -180,19 +170,13 @@ pub fn rfc8181(
|
||||
msg_bytes: Bytes,
|
||||
) -> HttpResponse {
|
||||
match SignedMessage::decode(msg_bytes, true) {
|
||||
Ok(msg) => {
|
||||
match server.read().handle_rfc8181_req(msg, handle.into_inner()) {
|
||||
Ok(captured) => {
|
||||
HttpResponse::build(StatusCode::OK)
|
||||
.content_type("application/rpki-publication")
|
||||
.body(captured.into_bytes())
|
||||
}
|
||||
Err(e) => {
|
||||
server_error(&Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => server_error(&Error::CmsError)
|
||||
Ok(msg) => match server.read().handle_rfc8181_req(msg, handle.into_inner()) {
|
||||
Ok(captured) => HttpResponse::build(StatusCode::OK)
|
||||
.content_type("application/rpki-publication")
|
||||
.body(captured.into_bytes()),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
},
|
||||
Err(_) => server_error(&Error::CmsError),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +186,7 @@ pub fn handle_delta(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
delta: Json<publication::PublishDelta>,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
let handle = handle.into_inner();
|
||||
let delta = delta.into_inner();
|
||||
@@ -214,42 +198,32 @@ pub fn handle_delta(
|
||||
|
||||
/// Processes a list request sent to the API.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn handle_list(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
) -> HttpResponse {
|
||||
pub fn handle_list(server: web::Data<AppServer>, auth: Auth, handle: Path<Handle>) -> HttpResponse {
|
||||
let handle = handle.into_inner();
|
||||
debug!("Received list request for {}", &handle);
|
||||
if_publication_allowed(&server, &handle, &auth, ||{
|
||||
if_publication_allowed(&server, &handle, &auth, || {
|
||||
match server.read().handle_list(&handle) {
|
||||
Ok(list) => render_json(list),
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//------------ Admin: Rfc8181 -----------------------------------------------
|
||||
|
||||
pub fn rfc8181_clients(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, ||{
|
||||
match server.read().rfc8181_clients() {
|
||||
Ok(clients) => render_json(clients),
|
||||
Err(e) => server_error(&Error::ServerError(e ))
|
||||
}
|
||||
pub fn rfc8181_clients(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || match server.read().rfc8181_clients() {
|
||||
Ok(clients) => render_json(clients),
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_rfc8181_client(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
client: Json<ClientInfo>
|
||||
client: Json<ClientInfo>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, ||{
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.read().add_rfc8181_client(client.into_inner()))
|
||||
})
|
||||
}
|
||||
@@ -257,49 +231,36 @@ pub fn add_rfc8181_client(
|
||||
pub fn repository_response(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
let handle = handle.into_inner();
|
||||
if_publication_allowed(&server, &handle, &auth, ||{
|
||||
if_publication_allowed(&server, &handle, &auth, || {
|
||||
match server.read().repository_response(&handle) {
|
||||
Ok(res) => {
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/xml")
|
||||
.body(res.encode_vec())
|
||||
},
|
||||
Ok(res) => HttpResponse::Ok()
|
||||
.content_type("application/xml")
|
||||
.body(res.encode_vec()),
|
||||
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//------------ Admin: TrustAnchor --------------------------------------------
|
||||
|
||||
pub fn ta_info(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, ||{
|
||||
match server.read().ta_info() {
|
||||
Some(ta) => render_json(ta),
|
||||
None => api_not_found()
|
||||
}
|
||||
pub fn ta_info(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || match server.read().ta_info() {
|
||||
Some(ta) => render_json(ta),
|
||||
None => api_not_found(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ta_init(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
pub fn ta_init(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.write().ta_init())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn republish_all(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
pub fn republish_all(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.read().republish_all())
|
||||
})
|
||||
@@ -307,67 +268,54 @@ pub fn republish_all(
|
||||
|
||||
pub fn tal(server: web::Data<AppServer>) -> HttpResponse {
|
||||
match server.read().ta_info() {
|
||||
Some(ta) => {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/plain")
|
||||
.body(format!("{}", ta.tal()))
|
||||
},
|
||||
None => api_not_found()
|
||||
Some(ta) => HttpResponse::Ok()
|
||||
.content_type("text/plain")
|
||||
.body(format!("{}", ta.tal())),
|
||||
None => api_not_found(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ta_cer(server: web::Data<AppServer>) -> HttpResponse {
|
||||
match server.read().trust_anchor_cert() {
|
||||
Some(cert) => {
|
||||
HttpResponse::Ok().body(cert.der_encoded().to_vec())
|
||||
},
|
||||
None => api_not_found()
|
||||
Some(cert) => HttpResponse::Ok().body(cert.der_encoded().to_vec()),
|
||||
None => api_not_found(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ta_add_child(
|
||||
server: web::Data<AppServer>,
|
||||
req: Json<AddChildRequest>,
|
||||
auth: Auth
|
||||
auth: Auth,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server.read().ta_add_child(req.into_inner()) {
|
||||
Ok(info) => render_json(info),
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//------------ Admin: CertAuth -----------------------------------------------
|
||||
|
||||
pub fn cas(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_json(server.read().cas())
|
||||
})
|
||||
pub fn cas(server: web::Data<AppServer>, auth: Auth) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || render_json(server.read().cas()))
|
||||
}
|
||||
|
||||
pub fn ca_init(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
ca_init: Json<CertAuthInit>
|
||||
ca_init: Json<CertAuthInit>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(server.write().ca_init(ca_init.into_inner()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ca_info(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
) -> HttpResponse {
|
||||
pub fn ca_info(server: web::Data<AppServer>, auth: Auth, handle: Path<Handle>) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server.read().ca_info(&handle.into_inner()) {
|
||||
Some(info) => render_json(info),
|
||||
None => api_not_found()
|
||||
None => api_not_found(),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -375,17 +323,15 @@ pub fn ca_info(
|
||||
pub fn ca_child_req(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>
|
||||
handle: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
let handle = handle.into_inner();
|
||||
if_api_allowed(&server, &auth, || {
|
||||
match server.read().ca_child_req(&handle) {
|
||||
Some(req) => {
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/xml")
|
||||
.body(req.encode_vec())
|
||||
},
|
||||
None => api_not_found()
|
||||
Some(req) => HttpResponse::Ok()
|
||||
.content_type("application/xml")
|
||||
.body(req.encode_vec()),
|
||||
None => api_not_found(),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -394,12 +340,14 @@ pub fn ca_add_parent(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
handle: Path<Handle>,
|
||||
parent: Json<AddParentRequest>
|
||||
parent: Json<AddParentRequest>,
|
||||
) -> HttpResponse {
|
||||
if_api_allowed(&server, &auth, || {
|
||||
render_empty_res(
|
||||
server.read()
|
||||
.ca_add_parent(handle.into_inner(), parent.into_inner()))
|
||||
server
|
||||
.read()
|
||||
.ca_add_parent(handle.into_inner(), parent.into_inner()),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -412,15 +360,14 @@ pub fn list(
|
||||
server: web::Data<AppServer>,
|
||||
auth: Auth,
|
||||
parent: Path<Handle>,
|
||||
child: Path<Handle>
|
||||
child: Path<Handle>,
|
||||
) -> HttpResponse {
|
||||
match server.read().list(
|
||||
&parent.into_inner(),
|
||||
&child.into_inner(),
|
||||
auth
|
||||
) {
|
||||
match server
|
||||
.read()
|
||||
.list(&parent.into_inner(), &child.into_inner(), auth)
|
||||
{
|
||||
Ok(entitlements) => render_json(entitlements),
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,16 +379,16 @@ pub fn issue(
|
||||
auth: Auth,
|
||||
parent: Path<Handle>,
|
||||
child: Path<Handle>,
|
||||
issue_req: Json<IssuanceRequest>
|
||||
issue_req: Json<IssuanceRequest>,
|
||||
) -> HttpResponse {
|
||||
match server.read().issue(
|
||||
&parent.into_inner(),
|
||||
&child.into_inner(),
|
||||
issue_req.into_inner(),
|
||||
auth
|
||||
auth,
|
||||
) {
|
||||
Ok(issued) => render_json(issued),
|
||||
Err(e) => server_error(&Error::ServerError(e))
|
||||
Err(e) => server_error(&Error::ServerError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,22 +400,15 @@ pub fn rfc6492(
|
||||
msg_bytes: Bytes,
|
||||
) -> HttpResponse {
|
||||
match SignedMessage::decode(msg_bytes, false) {
|
||||
Ok(msg) => {
|
||||
match server.read().rfc6492(
|
||||
parent.into_inner(),
|
||||
msg
|
||||
) {
|
||||
Ok(bytes) => {
|
||||
HttpResponse::build(StatusCode::OK)
|
||||
.content_type(rfc6492::CONTENT_TYPE)
|
||||
.body(bytes)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error processing RFC6492 req: {}", e);
|
||||
server_error(&Error::ServerError(e))
|
||||
}
|
||||
Ok(msg) => match server.read().rfc6492(parent.into_inner(), msg) {
|
||||
Ok(bytes) => HttpResponse::build(StatusCode::OK)
|
||||
.content_type(rfc6492::CONTENT_TYPE)
|
||||
.body(bytes),
|
||||
Err(e) => {
|
||||
error!("Error processing RFC6492 req: {}", e);
|
||||
server_error(&Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Error processing RFC6492 req: {}", e);
|
||||
server_error(&Error::CmsError)
|
||||
@@ -476,17 +416,12 @@ pub fn rfc6492(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ Serving RRDP --------------------------------------------------
|
||||
|
||||
pub fn current_snapshot_json(_server: web::Data<AppServer>) -> HttpResponse {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -502,7 +437,7 @@ pub enum Error {
|
||||
CmsError,
|
||||
|
||||
#[display(fmt = "Invalid publisher request")]
|
||||
PublisherRequestError
|
||||
PublisherRequestError,
|
||||
}
|
||||
|
||||
/// Translate an error to an HTTP Status Code
|
||||
@@ -527,7 +462,7 @@ impl ErrorToStatus for Error {
|
||||
Error::ServerError(e) => e.status(),
|
||||
Error::JsonError(_) => StatusCode::BAD_REQUEST,
|
||||
Error::CmsError => StatusCode::BAD_REQUEST,
|
||||
Error::PublisherRequestError => StatusCode::BAD_REQUEST
|
||||
Error::PublisherRequestError => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -558,7 +493,6 @@ impl ErrorToStatus for krill_pubd::Error {
|
||||
krill_pubd::Error::AggregateStoreError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ErrorToStatus for PublisherError {
|
||||
@@ -582,7 +516,7 @@ impl ErrorToStatus for ca::ServerError<OpenSslSigner> {
|
||||
fn status(&self) -> StatusCode {
|
||||
match self {
|
||||
ca::ServerError::CertAuth(e) => e.status(),
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -591,22 +525,21 @@ impl ErrorToStatus for ca::Error {
|
||||
fn status(&self) -> StatusCode {
|
||||
match self {
|
||||
ca::Error::Unauthorized(_) => StatusCode::FORBIDDEN,
|
||||
ca::Error::SignerError(_) | ca::Error::KeyStatusChange(_,_) =>
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
_ => StatusCode::BAD_REQUEST
|
||||
ca::Error::SignerError(_) | ca::Error::KeyStatusChange(_, _) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl ToErrorCode for Error {
|
||||
fn code(&self) -> ErrorCode {
|
||||
match self {
|
||||
Error::ServerError(e) => e.code(),
|
||||
Error::JsonError(_) => ErrorCode::InvalidJson,
|
||||
Error::CmsError => ErrorCode::InvalidCms,
|
||||
Error::PublisherRequestError => ErrorCode::InvalidPublisherRequest
|
||||
Error::PublisherRequestError => ErrorCode::InvalidPublisherRequest,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -653,7 +586,7 @@ impl ToErrorCode for VerificationError {
|
||||
match self {
|
||||
VerificationError::NoObjectForHashAndOrUri(_) => ErrorCode::NoObjectForHashAndOrUri,
|
||||
VerificationError::ObjectAlreadyPresent(_) => ErrorCode::ObjectAlreadyPresent,
|
||||
VerificationError::UriOutsideJail(_, _) => ErrorCode::UriOutsideJail
|
||||
VerificationError::UriOutsideJail(_, _) => ErrorCode::UriOutsideJail,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -661,7 +594,7 @@ impl ToErrorCode for VerificationError {
|
||||
impl ToErrorCode for RrdpServerError {
|
||||
fn code(&self) -> ErrorCode {
|
||||
match self {
|
||||
RrdpServerError::IoError(_) => ErrorCode::Persistence
|
||||
RrdpServerError::IoError(_) => ErrorCode::Persistence,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -670,7 +603,7 @@ impl ToErrorCode for ca::ServerError<OpenSslSigner> {
|
||||
fn code(&self) -> ErrorCode {
|
||||
match self {
|
||||
ca::ServerError::CertAuth(e) => e.code(),
|
||||
_ => ErrorCode::CaServerError
|
||||
_ => ErrorCode::CaServerError,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -681,13 +614,11 @@ impl ToErrorCode for ca::Error {
|
||||
ca::Error::DuplicateChild(_) => ErrorCode::DuplicateChild,
|
||||
ca::Error::MustHaveResources => ErrorCode::ChildNeedsResources,
|
||||
ca::Error::MissingResources => ErrorCode::ChildOverclaims,
|
||||
_ => ErrorCode::CaServerError
|
||||
_ => ErrorCode::CaServerError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Error {
|
||||
fn to_error_response(&self) -> ErrorResponse {
|
||||
self.code().clone().into()
|
||||
@@ -699,4 +630,4 @@ impl actix_web::ResponseError for Error {
|
||||
HttpResponse::build(self.status())
|
||||
.body(serde_json::to_string(&self.to_error_response()).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub mod server;
|
||||
pub mod ssl;
|
||||
pub mod statics;
|
||||
pub mod statics;
|
||||
|
||||
+49
-86
@@ -3,48 +3,27 @@
|
||||
//! Here we deal with booting and setup, and once active deal with parsing
|
||||
//! arguments and routing of requests, typically handing off to the
|
||||
//! daemon::api::endpoints functions for processing and responding.
|
||||
use std::io;
|
||||
use std::fs::File;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
RwLock,
|
||||
RwLockReadGuard,
|
||||
RwLockWriteGuard
|
||||
};
|
||||
use std::io;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use actix_web::{
|
||||
App,
|
||||
FromRequest,
|
||||
HttpResponse,
|
||||
HttpServer,
|
||||
};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{guard, middleware, web};
|
||||
use actix_web::web::{
|
||||
delete,
|
||||
get,
|
||||
post,
|
||||
scope,
|
||||
Path
|
||||
};
|
||||
use actix_session::CookieSession;
|
||||
use openssl::ssl::{SslMethod, SslAcceptor, SslAcceptorBuilder, SslFiletype};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{delete, get, post, scope, Path};
|
||||
use actix_web::{guard, middleware, web};
|
||||
use actix_web::{App, FromRequest, HttpResponse, HttpServer};
|
||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||
|
||||
use bcder::decode;
|
||||
|
||||
use krill_commons::api::publication;
|
||||
|
||||
use crate::auth::{
|
||||
AUTH_COOKIE_NAME,
|
||||
is_logged_in,
|
||||
login,
|
||||
logout
|
||||
};
|
||||
use crate::auth::{is_logged_in, login, logout, AUTH_COOKIE_NAME};
|
||||
use crate::config::Config;
|
||||
use crate::endpoints;
|
||||
use crate::endpoints::*;
|
||||
use crate::http::statics::WithStaticContent;
|
||||
use crate::http::ssl;
|
||||
use crate::http::statics::WithStaticContent;
|
||||
use crate::krillserver;
|
||||
use crate::krillserver::KrillServer;
|
||||
|
||||
@@ -63,10 +42,7 @@ impl AppServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn start(config: &Config) -> Result<(), Error> {
|
||||
|
||||
let server = {
|
||||
let krill = KrillServer::build(
|
||||
&config.data_dir,
|
||||
@@ -85,45 +61,40 @@ pub fn start(config: &Config) -> Result<(), Error> {
|
||||
App::new()
|
||||
.data(server.clone())
|
||||
.wrap(middleware::Logger::default())
|
||||
.wrap(CookieSession::signed(&[0; 32])
|
||||
.name(AUTH_COOKIE_NAME)
|
||||
.secure(true))
|
||||
.wrap(
|
||||
CookieSession::signed(&[0; 32])
|
||||
.name(AUTH_COOKIE_NAME)
|
||||
.secure(true),
|
||||
)
|
||||
.route("/health", get().to(endpoints::health))
|
||||
|
||||
// API end-points
|
||||
.service(
|
||||
scope("/api/v1")
|
||||
.route("/health", get().to(api_health))
|
||||
|
||||
.route("/publishers", get().to(publishers))
|
||||
.route("/publishers", post().to(add_publisher))
|
||||
.route("/publishers/{handle}", get().to(publisher_details))
|
||||
.route("/publishers/{handle}", delete().to(deactivate_publisher))
|
||||
|
||||
.route("/rfc8181/clients", get().to(rfc8181_clients))
|
||||
.route("/rfc8181/clients", post().to(add_rfc8181_client))
|
||||
.data(web::Bytes::configure(|cfg| {
|
||||
cfg.limit(256 * 1024 * 1024)
|
||||
}))
|
||||
.route("/rfc8181/{handle}/response.xml", get().to(repository_response))
|
||||
|
||||
.data(web::Bytes::configure(|cfg| cfg.limit(256 * 1024 * 1024)))
|
||||
.route(
|
||||
"/rfc8181/{handle}/response.xml",
|
||||
get().to(repository_response),
|
||||
)
|
||||
.route("/trustanchor", get().to(ta_info))
|
||||
.route("/trustanchor", post().to(ta_init))
|
||||
.route("/trustanchor/children", post().to(ta_add_child))
|
||||
|
||||
.route("/cas", post().to(ca_init))
|
||||
.route("/cas", get().to(cas))
|
||||
.route("/cas/{handle}", get().to(ca_info))
|
||||
.route("/cas/{handle}/child_request", get().to(ca_child_req))
|
||||
.route("/cas/{handle}/parents", post().to(ca_add_parent))
|
||||
|
||||
.route("/republish", post().to(republish_all))
|
||||
.route("/republish", post().to(republish_all)),
|
||||
)
|
||||
|
||||
// Public TA related methods
|
||||
.route("/ta/ta.tal", get().to(tal))
|
||||
.route("/ta/ta.cer", get().to(ta_cer))
|
||||
|
||||
// Publication by (embedded) clients
|
||||
.route("/publication/{handle}", get().to(handle_list))
|
||||
.route("/publication/{handle}", post().to(handle_delta))
|
||||
@@ -131,31 +102,26 @@ pub fn start(config: &Config) -> Result<(), Error> {
|
||||
cfg.limit(256 * 1024 * 1024)
|
||||
}))
|
||||
.route("/rfc8181/{handle}", post().to(rfc8181))
|
||||
|
||||
// Provisioning for remote krill clients
|
||||
.route("/provisioning/{parent}/{child}/list", get().to(list))
|
||||
.route("/provisioning/{parent}/{child}/issue", post().to(issue))
|
||||
|
||||
// Provisioning for rfc6492 clients
|
||||
.route("/rfc6492/{handle}", post().to(rfc6492))
|
||||
|
||||
|
||||
// UI support
|
||||
.route("/ui/is_logged_in", get().to(is_logged_in))
|
||||
.route("/ui/login", post().to(login))
|
||||
.route("/ui/logout", post().to(logout))
|
||||
|
||||
// RRDP repository
|
||||
.route("/rrdp/{path:.*}", get().to(serve_rrdp_files))
|
||||
|
||||
.route("/", get().to(|| {
|
||||
HttpResponse::Found()
|
||||
.header("location", "/ui/index.html")
|
||||
.finish()
|
||||
}))
|
||||
|
||||
.route(
|
||||
"/",
|
||||
get().to(|| {
|
||||
HttpResponse::Found()
|
||||
.header("location", "/ui/index.html")
|
||||
.finish()
|
||||
}),
|
||||
)
|
||||
.add_statics()
|
||||
|
||||
// default
|
||||
.default_service(
|
||||
// 404 for GET request
|
||||
@@ -168,13 +134,13 @@ pub fn start(config: &Config) -> Result<(), Error> {
|
||||
.to(HttpResponse::MethodNotAllowed),
|
||||
),
|
||||
)
|
||||
|
||||
}).bind_ssl(config.socket_addr(), https_builder)?.run()?;
|
||||
})
|
||||
.bind_ssl(config.socket_addr(), https_builder)?
|
||||
.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Used to set up HTTPS. Creates keypair and self signed certificate
|
||||
/// if config has 'use_ssl=test'.
|
||||
fn https_builder(config: &Config) -> Result<SslAcceptorBuilder, Error> {
|
||||
@@ -186,14 +152,13 @@ fn https_builder(config: &Config) -> Result<SslAcceptorBuilder, Error> {
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())
|
||||
.map_err(|e| Error::Other(format!("{}", e)))?;
|
||||
|
||||
builder.set_private_key_file(
|
||||
config.https_key_file(),
|
||||
SslFiletype::PEM
|
||||
).map_err(|e| Error::Other(format!("{}", e)))?;
|
||||
builder
|
||||
.set_private_key_file(config.https_key_file(), SslFiletype::PEM)
|
||||
.map_err(|e| Error::Other(format!("{}", e)))?;
|
||||
|
||||
builder.set_certificate_chain_file(
|
||||
config.https_cert_file()
|
||||
).map_err(|e| Error::Other(format!("{}", e)))?;
|
||||
builder
|
||||
.set_certificate_chain_file(config.https_cert_file())
|
||||
.map_err(|e| Error::Other(format!("{}", e)))?;
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
@@ -203,11 +168,7 @@ fn https_builder(config: &Config) -> Result<SslAcceptorBuilder, Error> {
|
||||
// See also:
|
||||
// https://github.com/actix/actix-website/blob/master/content/docs/static-files.md
|
||||
// https://www.keycdn.com/blog/http-cache-headers
|
||||
fn serve_rrdp_files(
|
||||
server: web::Data<AppServer>,
|
||||
path: Path<String>
|
||||
) -> HttpResponse
|
||||
{
|
||||
fn serve_rrdp_files(server: web::Data<AppServer>, path: Path<String>) -> HttpResponse {
|
||||
let mut full_path = server.read().rrdp_base_path();
|
||||
full_path.push(path.into_inner());
|
||||
match File::open(full_path) {
|
||||
@@ -217,14 +178,11 @@ fn serve_rrdp_files(
|
||||
file.read_to_end(&mut buffer).unwrap();
|
||||
|
||||
HttpResponse::build(StatusCode::OK).body(buffer)
|
||||
},
|
||||
_ => {
|
||||
HttpResponse::build(StatusCode::NOT_FOUND).finish()
|
||||
}
|
||||
_ => HttpResponse::build(StatusCode::NOT_FOUND).finish(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -250,15 +208,21 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(e: serde_json::Error) -> Self { Error::JsonError(e) }
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
Error::JsonError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self { Error::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<krillserver::Error> for Error {
|
||||
fn from(e: krillserver::Error) -> Self { Error::ServerError(e) }
|
||||
fn from(e: krillserver::Error) -> Self {
|
||||
Error::ServerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
@@ -269,8 +233,7 @@ impl std::error::Error for Error {
|
||||
|
||||
impl actix_web::ResponseError for Error {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(format!("{}", self))
|
||||
HttpResponse::build(StatusCode::INTERNAL_SERVER_ERROR).body(format!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+54
-89
@@ -1,43 +1,23 @@
|
||||
//! Some helper stuff for creating a private key and certificate for HTTPS
|
||||
//! in case they are not provided
|
||||
|
||||
use std::io::Write;
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use bcder::{
|
||||
BitString,
|
||||
Mode,
|
||||
Tag
|
||||
};
|
||||
use bcder::encode::{Constructed, PrimitiveContent, Values};
|
||||
use bcder::{decode, encode};
|
||||
use bcder::encode::{
|
||||
Constructed,
|
||||
Values,
|
||||
PrimitiveContent
|
||||
};
|
||||
use bcder::{BitString, Mode, Tag};
|
||||
use bytes::Bytes;
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::{PKey, Private};
|
||||
use openssl::rsa::Rsa;
|
||||
use openssl::hash::MessageDigest;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rpki::cert::ext::{
|
||||
AuthorityKeyIdentifier,
|
||||
BasicCa,
|
||||
SubjectKeyIdentifier
|
||||
};
|
||||
use rpki::crypto::{
|
||||
PublicKey,
|
||||
Signature,
|
||||
SignatureAlgorithm
|
||||
};
|
||||
use rpki::x509::{
|
||||
Name,
|
||||
Validity
|
||||
};
|
||||
use rpki::cert::ext::{AuthorityKeyIdentifier, BasicCa, SubjectKeyIdentifier};
|
||||
use rpki::crypto::{PublicKey, Signature, SignatureAlgorithm};
|
||||
use rpki::x509::{Name, Validity};
|
||||
|
||||
use krill_commons::util::file;
|
||||
|
||||
|
||||
const KEY_SIZE: u32 = 2048;
|
||||
pub const HTTPS_SUB_DIR: &str = "ssl";
|
||||
pub const KEY_FILE: &str = "key.pem";
|
||||
@@ -52,7 +32,7 @@ pub fn create_key_cert_if_needed(data_dir: &PathBuf) -> Result<(), Error> {
|
||||
let key_file_path = file::file_path(&https_dir, KEY_FILE);
|
||||
let cert_file_path = file::file_path(&https_dir, CERT_FILE);
|
||||
|
||||
if ! key_file_path.exists() || ! cert_file_path.exists() {
|
||||
if !key_file_path.exists() || !cert_file_path.exists() {
|
||||
create_key_and_cert(&https_dir)
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -63,7 +43,7 @@ pub fn create_key_cert_if_needed(data_dir: &PathBuf) -> Result<(), Error> {
|
||||
/// Only call this in case there is no current key and certificate file
|
||||
/// present, or have your files ruthlessly overwritten!
|
||||
fn create_key_and_cert(https_dir: &PathBuf) -> Result<(), Error> {
|
||||
if ! https_dir.exists() {
|
||||
if !https_dir.exists() {
|
||||
file::create_dir(&https_dir)?;
|
||||
}
|
||||
|
||||
@@ -74,13 +54,12 @@ fn create_key_and_cert(https_dir: &PathBuf) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
//------------ HttpsSigner ---------------------------------------------------
|
||||
|
||||
/// Signer specifically for generating an HTTPS key pair and certificate, and
|
||||
/// saving them both as PEM files in a directory.
|
||||
struct HttpsSigner {
|
||||
private: PKey<Private>
|
||||
private: PKey<Private>,
|
||||
}
|
||||
|
||||
impl HttpsSigner {
|
||||
@@ -102,53 +81,43 @@ impl HttpsSigner {
|
||||
|
||||
fn public_key_info(&self) -> Result<PublicKey, Error> {
|
||||
let mut b = Bytes::from(
|
||||
self.private.rsa().unwrap().public_key_to_der()
|
||||
.map_err(|e| { Error::OpenSslError(e) })?
|
||||
self.private
|
||||
.rsa()
|
||||
.unwrap()
|
||||
.public_key_to_der()
|
||||
.map_err(|e| Error::OpenSslError(e))?,
|
||||
);
|
||||
let pk = PublicKey::decode(&mut b)
|
||||
.map_err(|e| { Error::DecodeError(e)})?;
|
||||
let pk = PublicKey::decode(&mut b).map_err(|e| Error::DecodeError(e))?;
|
||||
Ok(pk)
|
||||
}
|
||||
|
||||
fn sign(&self, data: &Bytes) -> Result<Signature, Error> {
|
||||
|
||||
let mut signer = ::openssl::sign::Signer::new(
|
||||
MessageDigest::sha256(),
|
||||
&self.private
|
||||
)?;
|
||||
let mut signer = ::openssl::sign::Signer::new(MessageDigest::sha256(), &self.private)?;
|
||||
|
||||
signer.update(data.as_ref())?;
|
||||
|
||||
let signature_bytes = signer.sign_to_vec()?;
|
||||
|
||||
let signature = Signature::new(
|
||||
SignatureAlgorithm::default(),
|
||||
Bytes::from(signature_bytes)
|
||||
);
|
||||
let signature = Signature::new(SignatureAlgorithm::default(), Bytes::from(signature_bytes));
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
/// Saves a self-signed certificate so that actix can use it.
|
||||
fn save_certificate(&mut self, https_dir: &PathBuf) -> Result<(), Error> {
|
||||
|
||||
let pub_key = self.public_key_info()?;
|
||||
let tbs_cert = TbsHttpsCertificate::from(&pub_key);
|
||||
|
||||
let encoded_tbs = tbs_cert.encode().to_captured(Mode::Der);
|
||||
let (_, signature) = self.sign(encoded_tbs.as_ref())?.unwrap();
|
||||
|
||||
let signature = BitString::new(
|
||||
0,
|
||||
signature
|
||||
);
|
||||
let signature = BitString::new(0, signature);
|
||||
|
||||
let encoded_cert = encode::sequence(
|
||||
(
|
||||
encoded_tbs,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode()
|
||||
)
|
||||
).to_captured(Mode::Der);
|
||||
let encoded_cert = encode::sequence((
|
||||
encoded_tbs,
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
signature.encode(),
|
||||
))
|
||||
.to_captured(Mode::Der);
|
||||
|
||||
let cert_pem = base64::encode(&encoded_cert);
|
||||
|
||||
@@ -191,7 +160,7 @@ struct TbsHttpsCertificate {
|
||||
subject_public_key_info: PublicKey,
|
||||
// issuerUniqueID is not used
|
||||
// subjectUniqueID is not used
|
||||
extensions: HttpsCertExtensions
|
||||
extensions: HttpsCertExtensions,
|
||||
}
|
||||
|
||||
impl From<&PublicKey> for TbsHttpsCertificate {
|
||||
@@ -206,35 +175,37 @@ impl From<&PublicKey> for TbsHttpsCertificate {
|
||||
let extensions = HttpsCertExtensions::from(pk);
|
||||
|
||||
TbsHttpsCertificate {
|
||||
issuer, validity, subject, subject_public_key_info, extensions
|
||||
issuer,
|
||||
validity,
|
||||
subject,
|
||||
subject_public_key_info,
|
||||
extensions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TbsHttpsCertificate {
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
|
||||
encode::sequence((
|
||||
(
|
||||
Constructed::new(
|
||||
Tag::CTX_0,
|
||||
2_i32.encode() // Version 3 is encoded as 2
|
||||
2_i32.encode(), // Version 3 is encoded as 2
|
||||
),
|
||||
1_i32.encode(),
|
||||
SignatureAlgorithm::default().x509_encode(),
|
||||
self.issuer.encode_ref()
|
||||
self.issuer.encode_ref(),
|
||||
),
|
||||
(
|
||||
self.validity.encode(),
|
||||
self.subject.encode_ref(),
|
||||
self.subject_public_key_info.clone().encode(),
|
||||
self.extensions.encode()
|
||||
)
|
||||
self.extensions.encode(),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ IdExtensions --------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -259,27 +230,25 @@ impl From<&PublicKey> for HttpsCertExtensions {
|
||||
let authority_key_id = AuthorityKeyIdentifier::new(pk);
|
||||
|
||||
HttpsCertExtensions {
|
||||
basic_ca, subject_key_id, authority_key_id
|
||||
basic_ca,
|
||||
subject_key_id,
|
||||
authority_key_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Encoding
|
||||
impl HttpsCertExtensions {
|
||||
|
||||
pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
|
||||
Constructed::new(
|
||||
Tag::CTX_3,
|
||||
encode::sequence(
|
||||
(
|
||||
self.basic_ca.encode(),
|
||||
self.subject_key_id.clone().encode(),
|
||||
self.authority_key_id.clone().encode()
|
||||
)
|
||||
)
|
||||
encode::sequence((
|
||||
self.basic_ca.encode(),
|
||||
self.subject_key_id.clone().encode(),
|
||||
self.authority_key_id.clone().encode(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
@@ -295,8 +264,8 @@ pub enum Error {
|
||||
#[display(fmt = "{}", _0)]
|
||||
DecodeError(decode::Error),
|
||||
|
||||
#[display(fmt="Could not make certificate")]
|
||||
BuildError
|
||||
#[display(fmt = "Could not make certificate")]
|
||||
BuildError,
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for Error {
|
||||
@@ -318,13 +287,12 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use actix_web::*;
|
||||
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
|
||||
use krill_commons::util::test;
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
#[test]
|
||||
fn should_create_key_and_cert_and_start_server() {
|
||||
test::test_under_tmp(|d| {
|
||||
|
||||
let mut p_key_file_path = d.clone();
|
||||
p_key_file_path.push("ssl");
|
||||
p_key_file_path.push("key.pem");
|
||||
@@ -335,21 +303,18 @@ mod tests {
|
||||
|
||||
create_key_cert_if_needed(&d).unwrap();
|
||||
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(
|
||||
SslMethod::tls()
|
||||
).unwrap();
|
||||
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||
|
||||
builder.set_private_key_file(
|
||||
p_key_file_path,
|
||||
SslFiletype::PEM
|
||||
).unwrap();
|
||||
builder
|
||||
.set_private_key_file(p_key_file_path, SslFiletype::PEM)
|
||||
.unwrap();
|
||||
|
||||
builder.set_certificate_chain_file(cert_file_path).unwrap();
|
||||
|
||||
HttpServer::new(|| {App::new()})
|
||||
HttpServer::new(|| App::new())
|
||||
.bind_ssl("localhost:8443", builder)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+61
-86
@@ -1,51 +1,32 @@
|
||||
use actix_web::{
|
||||
App,
|
||||
Error,
|
||||
HttpResponse,
|
||||
web
|
||||
};
|
||||
use actix_web::dev::{
|
||||
MessageBody,
|
||||
ServiceRequest,
|
||||
ServiceResponse
|
||||
};
|
||||
use actix_service::NewService;
|
||||
|
||||
use actix_web::dev::{MessageBody, ServiceRequest, ServiceResponse};
|
||||
use actix_web::{web, App, Error, HttpResponse};
|
||||
|
||||
/// This trait allows for adding static content.
|
||||
/// Using a trait here so that it can be used fluidly in the
|
||||
/// building of the 'App'.
|
||||
pub trait WithStaticContent {
|
||||
|
||||
/// Add a single static resource.
|
||||
fn add_static(
|
||||
self,
|
||||
static_content: &'static StaticContent
|
||||
) -> Self;
|
||||
fn add_static(self, static_content: &'static StaticContent) -> Self;
|
||||
|
||||
/// Add all static resources defined in this module.
|
||||
fn add_statics(
|
||||
self,
|
||||
) -> Self;
|
||||
fn add_statics(self) -> Self;
|
||||
}
|
||||
|
||||
/// Implementation for the App type that is returned when App::new()
|
||||
/// is used.
|
||||
impl<T, B> WithStaticContent for App<T, B>
|
||||
where
|
||||
B: MessageBody,
|
||||
T: NewService<
|
||||
Config = (),
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
where
|
||||
B: MessageBody,
|
||||
T: NewService<
|
||||
Config = (),
|
||||
Request = ServiceRequest,
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
{
|
||||
fn add_static(
|
||||
self,
|
||||
static_content: &'static StaticContent
|
||||
) -> Self {
|
||||
fn add_static(self, static_content: &'static StaticContent) -> Self {
|
||||
self.route(
|
||||
static_content.web_path,
|
||||
web::get().to(move || {
|
||||
@@ -53,26 +34,20 @@ impl<T, B> WithStaticContent for App<T, B>
|
||||
.content_type(static_content.ctype)
|
||||
.header("Cache-Control", "max-age: 86400")
|
||||
.body(static_content.content)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn add_statics(self) -> Self {
|
||||
self
|
||||
.add_static(&NOT_FOUND)
|
||||
self.add_static(&NOT_FOUND)
|
||||
.add_static(&INDEX)
|
||||
|
||||
.add_static(&FAVICON)
|
||||
|
||||
.add_static(&APP_JS)
|
||||
.add_static(&APP_JS_MAP)
|
||||
|
||||
.add_static(&APP_CSS)
|
||||
|
||||
.add_static(&IMG_KRILL_LOG)
|
||||
.add_static(&IMG_ROUTE_LEFT)
|
||||
.add_static(&IMG_ROUTE_RIGHT)
|
||||
|
||||
.add_static(&FONTS_EL_ICONS)
|
||||
.add_static(&FONTS_LATIN_100)
|
||||
.add_static(&FONTS_LATIN_100_2)
|
||||
@@ -116,12 +91,12 @@ pub struct StaticContent {
|
||||
|
||||
//------------ Definition of Statics -----------------------------------------
|
||||
|
||||
static HTML: &'static str = "text/html";
|
||||
static FAV: &'static str = "image/x-icon";
|
||||
static JS: &'static str = "application/javascript";
|
||||
static CSS: &'static str = "text/css";
|
||||
static SVG: &'static str = "image/svg+xml";
|
||||
static WOFF: &'static str = "font/woff";
|
||||
static HTML: &'static str = "text/html";
|
||||
static FAV: &'static str = "image/x-icon";
|
||||
static JS: &'static str = "application/javascript";
|
||||
static CSS: &'static str = "text/css";
|
||||
static SVG: &'static str = "image/svg+xml";
|
||||
static WOFF: &'static str = "font/woff";
|
||||
static WOFF2: &'static str = "font/woff2";
|
||||
|
||||
static NOT_FOUND: StaticContent = StaticContent {
|
||||
@@ -132,190 +107,190 @@ static NOT_FOUND: StaticContent = StaticContent {
|
||||
static INDEX: StaticContent = StaticContent {
|
||||
web_path: "/ui/index.html",
|
||||
content: include_bytes!("../../ui/dist/index.html"),
|
||||
ctype: HTML
|
||||
ctype: HTML,
|
||||
};
|
||||
static FAVICON: StaticContent = StaticContent {
|
||||
web_path: "/ui/favicon.ico",
|
||||
content: include_bytes!("../../ui/dist/favicon.ico"),
|
||||
ctype: FAV
|
||||
content: include_bytes!("../../ui/dist/favicon.ico"),
|
||||
ctype: FAV,
|
||||
};
|
||||
static APP_JS: StaticContent = StaticContent {
|
||||
web_path: "/ui/js/app.js",
|
||||
content: include_bytes!("../../ui/dist/js/app.js"),
|
||||
ctype: JS
|
||||
ctype: JS,
|
||||
};
|
||||
static APP_JS_MAP: StaticContent = StaticContent {
|
||||
web_path: "/ui/js/app.js.map",
|
||||
content: include_bytes!("../../ui/dist/js/app.js.map"),
|
||||
ctype: JS
|
||||
ctype: JS,
|
||||
};
|
||||
static APP_CSS: StaticContent = StaticContent {
|
||||
web_path: "/ui/css/app.css",
|
||||
content: include_bytes!("../../ui/dist/css/app.css"),
|
||||
ctype: CSS
|
||||
ctype: CSS,
|
||||
};
|
||||
static IMG_KRILL_LOG: StaticContent = StaticContent {
|
||||
web_path: "/ui/img/krill_logo_white.svg",
|
||||
content: include_bytes!("../../ui/dist/img/krill_logo_white.svg"),
|
||||
ctype: SVG
|
||||
ctype: SVG,
|
||||
};
|
||||
static IMG_ROUTE_LEFT: StaticContent = StaticContent {
|
||||
web_path: "/ui/img/route_left.svg",
|
||||
content: include_bytes!("../../ui/dist/img/route_left.svg"),
|
||||
ctype: SVG
|
||||
ctype: SVG,
|
||||
};
|
||||
static IMG_ROUTE_RIGHT: StaticContent = StaticContent {
|
||||
web_path: "/ui/img/route_right.svg",
|
||||
content: include_bytes!("../../ui/dist/img/route_right.svg"),
|
||||
ctype: SVG
|
||||
ctype: SVG,
|
||||
};
|
||||
static FONTS_EL_ICONS: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/element-icons.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/element-icons.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_100: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-100.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-100.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_100_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-100.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-100.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_300: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-300.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-300.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_300_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-300.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-300.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_400: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-400.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-400.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_400_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-400.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-400.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_700: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-700.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-700.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_700_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-700.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-700.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_900: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-900.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-900.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_900_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-900.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-900.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_100_IT: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-100italic.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-100italic.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_100_IT_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-100italic.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-100italic.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_300_IT: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-300italic.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-300italic.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_300_IT_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-300italic.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-300italic.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_400_IT: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-400italic.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-400italic.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_400_IT_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-400italic.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-400italic.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_700_IT: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-700italic.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-700italic.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_LATIN_700_IT_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-700italic.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-700italic.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_LATIN_900_IT: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/lato-latin-900italic.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/lato-latin-900italic.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_200: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-200.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-200.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_200_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-200.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-200.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_300: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-300.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-300.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_300_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-300.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-300.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_400: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-400.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-400.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_400_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-400.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-400.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_700: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-700.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-700.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_700_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-700.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-700.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_900: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-900.woff",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-900.woff"),
|
||||
ctype: WOFF
|
||||
ctype: WOFF,
|
||||
};
|
||||
static FONTS_SOURCE_CODE_900_2: StaticContent = StaticContent {
|
||||
web_path: "/ui/fonts/source-code-pro-latin-900.woff2",
|
||||
content: include_bytes!("../../ui/dist/fonts/source-code-pro-latin-900.woff2"),
|
||||
ctype: WOFF2
|
||||
ctype: WOFF2,
|
||||
};
|
||||
|
||||
+111
-175
@@ -3,34 +3,33 @@ use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytes::Bytes;
|
||||
use bcder::Captured;
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
|
||||
use krill_commons::api::{publication, Entitlements, IssuanceRequest, IssuanceResponse};
|
||||
use krill_commons::api::admin;
|
||||
use krill_commons::api::admin::{Handle, Token, CertAuthInit, CertAuthPubMode, ParentCaContact, AddChildRequest, AddParentRequest};
|
||||
use krill_commons::api::ca::{TrustAnchorInfo, RcvdCert, CertAuthList, CertAuthInfo};
|
||||
use krill_commons::api::admin::{
|
||||
AddChildRequest, AddParentRequest, CertAuthInit, CertAuthPubMode, Handle, ParentCaContact,
|
||||
Token,
|
||||
};
|
||||
use krill_commons::api::ca::{CertAuthInfo, CertAuthList, RcvdCert, TrustAnchorInfo};
|
||||
use krill_commons::api::publication::PublishRequest;
|
||||
use krill_commons::util::softsigner::{OpenSslSigner, SignerError};
|
||||
use krill_commons::api::{publication, Entitlements, IssuanceRequest, IssuanceResponse};
|
||||
use krill_commons::remote::api::ClientInfo;
|
||||
use krill_commons::remote::proxy;
|
||||
use krill_commons::remote::proxy::ProxyServer;
|
||||
use krill_commons::remote::rfc8181::ReplyMessage;
|
||||
use krill_commons::remote::rfc8183::{RepositoryResponse, ChildRequest};
|
||||
use krill_commons::remote::rfc8183::{ChildRequest, RepositoryResponse};
|
||||
use krill_commons::remote::sigmsg::SignedMessage;
|
||||
use krill_pubd::PubServer;
|
||||
use krill_commons::util::softsigner::{OpenSslSigner, SignerError};
|
||||
use krill_pubd::publishers::Publisher;
|
||||
use krill_pubd::PubServer;
|
||||
|
||||
use crate::ca::{
|
||||
self,
|
||||
ta_handle,
|
||||
};
|
||||
use crate::auth::{Auth, Authorizer};
|
||||
use crate::ca::{self, ta_handle};
|
||||
use crate::mq::EventQueueListener;
|
||||
use crate::scheduler::Scheduler;
|
||||
|
||||
|
||||
//------------ KrillServer ---------------------------------------------------
|
||||
|
||||
/// This is the master krill server that is doing all the orchestration
|
||||
@@ -66,8 +65,7 @@ pub struct KrillServer {
|
||||
|
||||
// Responsible for background tasks, e.g. re-publishing
|
||||
#[allow(dead_code)] // just need to keep this in scope
|
||||
scheduler: Scheduler
|
||||
|
||||
scheduler: Scheduler,
|
||||
}
|
||||
|
||||
/// # Set up and initialisation
|
||||
@@ -86,48 +84,32 @@ impl KrillServer {
|
||||
|
||||
let authorizer = Authorizer::new(token);
|
||||
|
||||
let pubserver = Arc::new(PubServer::build(
|
||||
base_uri.clone(),
|
||||
rrdp_base_uri.clone(),
|
||||
repo_dir,
|
||||
work_dir
|
||||
).map_err(Error::PubServer)?);
|
||||
let pubserver = Arc::new(
|
||||
PubServer::build(base_uri.clone(), rrdp_base_uri.clone(), repo_dir, work_dir)
|
||||
.map_err(Error::PubServer)?,
|
||||
);
|
||||
|
||||
|
||||
let proxy_server = ProxyServer::init(
|
||||
work_dir, &service_uri
|
||||
)?;
|
||||
let proxy_server = ProxyServer::init(work_dir, &service_uri)?;
|
||||
|
||||
let signer = OpenSslSigner::build(work_dir)?;
|
||||
|
||||
let event_queue = Arc::new(EventQueueListener::in_mem());
|
||||
|
||||
let caserver = Arc::new(ca::CaServer::build(
|
||||
work_dir,
|
||||
event_queue.clone(),
|
||||
signer
|
||||
)?);
|
||||
let caserver = Arc::new(ca::CaServer::build(work_dir, event_queue.clone(), signer)?);
|
||||
|
||||
let scheduler = Scheduler::build(
|
||||
event_queue,
|
||||
caserver.clone(),
|
||||
pubserver.clone()
|
||||
);
|
||||
let scheduler = Scheduler::build(event_queue, caserver.clone(), pubserver.clone());
|
||||
|
||||
Ok(
|
||||
KrillServer {
|
||||
service_uri,
|
||||
work_dir: work_dir.clone(),
|
||||
authorizer,
|
||||
pubserver,
|
||||
caserver,
|
||||
proxy_server,
|
||||
scheduler
|
||||
}
|
||||
)
|
||||
Ok(KrillServer {
|
||||
service_uri,
|
||||
work_dir: work_dir.clone(),
|
||||
authorizer,
|
||||
pubserver,
|
||||
caserver,
|
||||
proxy_server,
|
||||
scheduler,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn service_base_uri(&self) -> &uri::Https {
|
||||
&self.service_uri
|
||||
}
|
||||
@@ -138,21 +120,14 @@ impl KrillServer {
|
||||
self.authorizer.is_api_allowed(&token)
|
||||
}
|
||||
|
||||
pub fn is_api_allowed(
|
||||
&self,
|
||||
auth: &Auth
|
||||
) -> bool {
|
||||
pub fn is_api_allowed(&self, auth: &Auth) -> bool {
|
||||
match auth {
|
||||
Auth::User(name) => name == "admin",
|
||||
Auth::Bearer(token) => self.authorizer.is_api_allowed(&token)
|
||||
Auth::Bearer(token) => self.authorizer.is_api_allowed(&token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_publication_api_allowed(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
auth: &Auth
|
||||
) -> bool {
|
||||
pub fn is_publication_api_allowed(&self, handle: &Handle, auth: &Auth) -> bool {
|
||||
let allowed = match auth {
|
||||
Auth::User(name) => name == "admin",
|
||||
Auth::Bearer(token) => {
|
||||
@@ -169,46 +144,42 @@ impl KrillServer {
|
||||
if allowed {
|
||||
debug!("Access to publication api allowed")
|
||||
} else {
|
||||
warn!("Access to publication api disallowed for handle: {}, and auth: {}", handle, auth);
|
||||
warn!(
|
||||
"Access to publication api disallowed for handle: {}, and auth: {}",
|
||||
handle, auth
|
||||
);
|
||||
}
|
||||
|
||||
allowed
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// # Configure publishers
|
||||
impl KrillServer {
|
||||
|
||||
/// Returns all currently configured publishers. (excludes deactivated)
|
||||
pub fn publishers(
|
||||
&self
|
||||
) -> Vec<Handle> {
|
||||
pub fn publishers(&self) -> Vec<Handle> {
|
||||
self.pubserver.list_publishers()
|
||||
}
|
||||
|
||||
/// Adds the publishers, blows up if it already existed.
|
||||
pub fn add_publisher(
|
||||
&mut self,
|
||||
pbl_req: admin::PublisherRequest
|
||||
) -> EmptyRes {
|
||||
self.pubserver.create_publisher(pbl_req).map_err(Error::PubServer)
|
||||
pub fn add_publisher(&mut self, pbl_req: admin::PublisherRequest) -> EmptyRes {
|
||||
self.pubserver
|
||||
.create_publisher(pbl_req)
|
||||
.map_err(Error::PubServer)
|
||||
}
|
||||
|
||||
/// Removes a publisher, blows up if it didn't exist.
|
||||
pub fn deactivate_publisher(
|
||||
&mut self,
|
||||
handle: &Handle
|
||||
) -> EmptyRes {
|
||||
self.pubserver.deactivate_publisher(handle).map_err(Error::PubServer)
|
||||
pub fn deactivate_publisher(&mut self, handle: &Handle) -> EmptyRes {
|
||||
self.pubserver
|
||||
.deactivate_publisher(handle)
|
||||
.map_err(Error::PubServer)
|
||||
}
|
||||
|
||||
/// Returns an option for a publisher.
|
||||
pub fn publisher(
|
||||
&self,
|
||||
handle: &Handle
|
||||
) -> Result<Option<Arc<Publisher>>, Error> {
|
||||
self.pubserver.get_publisher(handle).map_err(Error::PubServer)
|
||||
pub fn publisher(&self, handle: &Handle) -> Result<Option<Arc<Publisher>>, Error> {
|
||||
self.pubserver
|
||||
.get_publisher(handle)
|
||||
.map_err(Error::PubServer)
|
||||
}
|
||||
|
||||
pub fn rrdp_base_path(&self) -> PathBuf {
|
||||
@@ -221,87 +192,75 @@ impl KrillServer {
|
||||
/// # Manage RFC8181 clients
|
||||
///
|
||||
impl KrillServer {
|
||||
pub fn rfc8181_clients(&self) ->Result<Vec<ClientInfo>, Error> {
|
||||
pub fn rfc8181_clients(&self) -> Result<Vec<ClientInfo>, Error> {
|
||||
self.proxy_server.list_clients().map_err(Error::ProxyServer)
|
||||
}
|
||||
|
||||
pub fn add_rfc8181_client(&self, client: ClientInfo) -> EmptyRes {
|
||||
self.proxy_server.add_client(client).map_err(Error::ProxyServer)
|
||||
self.proxy_server
|
||||
.add_client(client)
|
||||
.map_err(Error::ProxyServer)
|
||||
}
|
||||
|
||||
pub fn repository_response(&self, handle: &Handle) -> Result<RepositoryResponse, Error> {
|
||||
let publisher = self.publisher(handle)?
|
||||
let publisher = self
|
||||
.publisher(handle)?
|
||||
.ok_or_else(|| Error::ProxyServer(proxy::Error::UnknownClient(handle.clone())))?;
|
||||
|
||||
let sia_base = publisher.base_uri().clone();
|
||||
|
||||
let service_uri = format!(
|
||||
"{}rfc8181/{}",
|
||||
self.service_uri.to_string(),
|
||||
handle
|
||||
);
|
||||
let service_uri = format!("{}rfc8181/{}", self.service_uri.to_string(), handle);
|
||||
let service_uri = uri::Https::from_string(service_uri).unwrap();
|
||||
|
||||
let rrdp_notification_uri = format!(
|
||||
"{}rrdp/notification.xml",
|
||||
self.service_uri.to_string(),
|
||||
);
|
||||
let rrdp_notification_uri =
|
||||
format!("{}rrdp/notification.xml", self.service_uri.to_string(),);
|
||||
let rrdp_notification_uri = uri::Https::from_string(rrdp_notification_uri).unwrap();
|
||||
|
||||
self.proxy_server.response(
|
||||
handle,
|
||||
service_uri,
|
||||
sia_base,
|
||||
rrdp_notification_uri
|
||||
).map_err(Error::ProxyServer)
|
||||
self.proxy_server
|
||||
.response(handle, service_uri, sia_base, rrdp_notification_uri)
|
||||
.map_err(Error::ProxyServer)
|
||||
}
|
||||
|
||||
pub fn handle_rfc8181_req(
|
||||
&self,
|
||||
msg: SignedMessage,
|
||||
handle: Handle
|
||||
handle: Handle,
|
||||
) -> Result<Captured, Error> {
|
||||
debug!("Handling signed request for {}", &handle);
|
||||
match self.try_rfc8181_req(msg, handle) {
|
||||
Ok(captured) => Ok(captured),
|
||||
Err(Error::ProxyServer(e)) => {
|
||||
self.proxy_server.wrap_error(e).map_err(Error::ProxyServer)
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to handle the rfc8181 request, and error out in case of
|
||||
/// issues.
|
||||
fn try_rfc8181_req(
|
||||
&self,
|
||||
msg: SignedMessage,
|
||||
handle: Handle
|
||||
) -> Result<Captured, Error> {
|
||||
fn try_rfc8181_req(&self, msg: SignedMessage, handle: Handle) -> Result<Captured, Error> {
|
||||
let req = self.proxy_server.convert_rfc8181_req(msg, &handle)?;
|
||||
let reply = match req {
|
||||
PublishRequest::List => {
|
||||
ReplyMessage::ListReply(
|
||||
self.pubserver.list(&handle)?
|
||||
)
|
||||
},
|
||||
PublishRequest::List => ReplyMessage::ListReply(self.pubserver.list(&handle)?),
|
||||
PublishRequest::Delta(delta) => {
|
||||
self.pubserver.publish(&handle, delta)?;
|
||||
ReplyMessage::SuccessReply
|
||||
}
|
||||
};
|
||||
|
||||
self.proxy_server.sign_reply(reply).map_err(Error::ProxyServer)
|
||||
self.proxy_server
|
||||
.sign_reply(reply)
|
||||
.map_err(Error::ProxyServer)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Admin Trust Anchor
|
||||
///
|
||||
impl KrillServer {
|
||||
pub fn ta_info(&self) -> Option<TrustAnchorInfo> {
|
||||
pub fn ta_info(&self) -> Option<TrustAnchorInfo> {
|
||||
match self.caserver.get_trust_anchor() {
|
||||
Ok(ta) => ta.as_ta_info().ok(),
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +269,6 @@ impl KrillServer {
|
||||
}
|
||||
|
||||
pub fn ta_init(&mut self) -> EmptyRes {
|
||||
|
||||
let ta_handle = ta_handle();
|
||||
|
||||
let repo_info = self.pubserver.repo_info_for(&ta_handle)?;
|
||||
@@ -331,11 +289,9 @@ impl KrillServer {
|
||||
self.add_publisher(req)?;
|
||||
|
||||
// Add TA
|
||||
self.caserver.init_ta(
|
||||
repo_info,
|
||||
ta_aia,
|
||||
vec![ta_uri]
|
||||
).map_err(Error::CaServerError)?;
|
||||
self.caserver
|
||||
.init_ta(repo_info, ta_aia, vec![ta_uri])
|
||||
.map_err(Error::CaServerError)?;
|
||||
|
||||
// Force initial publication
|
||||
self.caserver.republish(&ta_handle)?;
|
||||
@@ -345,10 +301,7 @@ impl KrillServer {
|
||||
|
||||
/// Adds a child to the TA and returns the ParentCaInfo that the child
|
||||
/// will to contact this TA for resource requests.
|
||||
pub fn ta_add_child(
|
||||
&self,
|
||||
req: AddChildRequest
|
||||
) -> Result<ParentCaContact, Error> {
|
||||
pub fn ta_add_child(&self, req: AddChildRequest) -> Result<ParentCaContact, Error> {
|
||||
let contact = self.caserver.ta_add_child(req, &self.service_uri)?;
|
||||
Ok(contact)
|
||||
}
|
||||
@@ -373,15 +326,17 @@ impl KrillServer {
|
||||
|
||||
/// Returns the child request for a CA, or NONE if the CA cannot be found.
|
||||
pub fn ca_child_req(&self, handle: &Handle) -> Option<ChildRequest> {
|
||||
self.caserver.get_ca(handle).map(|ca| ca.child_request()).ok()
|
||||
self.caserver
|
||||
.get_ca(handle)
|
||||
.map(|ca| ca.child_request())
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn ca_init(&mut self, init: CertAuthInit) -> EmptyRes {
|
||||
|
||||
let (handle, token, pub_mode) = init.unwrap();
|
||||
|
||||
let repo_info = match pub_mode {
|
||||
CertAuthPubMode::Embedded => self.pubserver.repo_info_for(&handle)?
|
||||
CertAuthPubMode::Embedded => self.pubserver.repo_info_for(&handle)?,
|
||||
};
|
||||
let base_uri = repo_info.ca_repository("");
|
||||
|
||||
@@ -389,31 +344,18 @@ impl KrillServer {
|
||||
self.caserver.init_ca(&handle, token.clone(), repo_info)?;
|
||||
|
||||
// Add publisher
|
||||
let req = admin::PublisherRequest::new(
|
||||
handle.clone(),
|
||||
token.clone(),
|
||||
base_uri,
|
||||
);
|
||||
let req = admin::PublisherRequest::new(handle.clone(), token.clone(), base_uri);
|
||||
self.add_publisher(req)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ca_add_parent(
|
||||
&self,
|
||||
handle: Handle,
|
||||
parent: AddParentRequest
|
||||
) -> EmptyRes {
|
||||
pub fn ca_add_parent(&self, handle: Handle, parent: AddParentRequest) -> EmptyRes {
|
||||
self.caserver.ca_add_parent(handle, parent)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list(
|
||||
&self,
|
||||
parent: &Handle,
|
||||
child: &Handle,
|
||||
auth: Auth
|
||||
) -> KrillRes<Entitlements> {
|
||||
pub fn list(&self, parent: &Handle, child: &Handle, auth: Auth) -> KrillRes<Entitlements> {
|
||||
Ok(self.caserver.list(parent, child, &auth.into())?)
|
||||
}
|
||||
|
||||
@@ -422,24 +364,14 @@ impl KrillServer {
|
||||
parent: &Handle,
|
||||
child: &Handle,
|
||||
issue_req: IssuanceRequest,
|
||||
auth: Auth
|
||||
auth: Auth,
|
||||
) -> KrillRes<IssuanceResponse> {
|
||||
Ok(self.caserver.issue(
|
||||
parent,
|
||||
child,
|
||||
issue_req,
|
||||
auth.into()
|
||||
)?)
|
||||
Ok(self.caserver.issue(parent, child, issue_req, auth.into())?)
|
||||
}
|
||||
|
||||
pub fn rfc6492(
|
||||
&self,
|
||||
handle: Handle,
|
||||
msg: SignedMessage
|
||||
) -> KrillRes<Bytes> {
|
||||
pub fn rfc6492(&self, handle: Handle, msg: SignedMessage) -> KrillRes<Bytes> {
|
||||
Ok(self.caserver.rfc6492(&handle, msg)?)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// # Handle publication requests
|
||||
@@ -448,24 +380,18 @@ impl KrillServer {
|
||||
/// Handles a publish delta request sent to the API, or.. through
|
||||
/// the CmsProxy.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn handle_delta(
|
||||
&self,
|
||||
delta: publication::PublishDelta,
|
||||
handle: &Handle
|
||||
) -> EmptyRes {
|
||||
self.pubserver.publish(handle, delta).map_err(Error::PubServer)
|
||||
pub fn handle_delta(&self, delta: publication::PublishDelta, handle: &Handle) -> EmptyRes {
|
||||
self.pubserver
|
||||
.publish(handle, delta)
|
||||
.map_err(Error::PubServer)
|
||||
}
|
||||
|
||||
/// Handles a list request sent to the API, or.. through the CmsProxy.
|
||||
pub fn handle_list(
|
||||
&self,
|
||||
handle: &Handle
|
||||
) -> Result<publication::ListReply, Error> {
|
||||
pub fn handle_list(&self, handle: &Handle) -> Result<publication::ListReply, Error> {
|
||||
self.pubserver.list(handle).map_err(Error::PubServer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Response Aliases ----------------------------------------------
|
||||
|
||||
type KrillRes<T> = Result<T, Error>;
|
||||
@@ -476,40 +402,50 @@ type EmptyRes = KrillRes<()>;
|
||||
#[derive(Debug, Display)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum Error {
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
PubServer(krill_pubd::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
ProxyServer(proxy::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
SignerError(SignerError),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
CaServerError(ca::ServerError<OpenSslSigner>),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self { Error::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<krill_pubd::Error> for Error {
|
||||
fn from(e: krill_pubd::Error) -> Self { Error::PubServer(e) }
|
||||
fn from(e: krill_pubd::Error) -> Self {
|
||||
Error::PubServer(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proxy::Error> for Error {
|
||||
fn from(e: proxy::Error) -> Self { Error::ProxyServer(e) }
|
||||
fn from(e: proxy::Error) -> Self {
|
||||
Error::ProxyServer(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignerError> for Error {
|
||||
fn from(e: SignerError) -> Self { Error::SignerError(e) }
|
||||
fn from(e: SignerError) -> Self {
|
||||
Error::SignerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ca::ServerError<OpenSslSigner>> for Error {
|
||||
fn from(e: ca::ServerError<OpenSslSigner>) -> Self { Error::CaServerError(e) }
|
||||
fn from(e: ca::ServerError<OpenSslSigner>) -> Self {
|
||||
Error::CaServerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Tested through integration tests
|
||||
// Tested through integration tests
|
||||
|
||||
+12
-12
@@ -1,23 +1,26 @@
|
||||
extern crate actix_identity;
|
||||
extern crate actix_web;
|
||||
extern crate actix_service;
|
||||
extern crate actix_session;
|
||||
extern crate actix_web;
|
||||
extern crate base64;
|
||||
extern crate bytes;
|
||||
extern crate bcder;
|
||||
extern crate bytes;
|
||||
extern crate chrono;
|
||||
extern crate clap;
|
||||
extern crate clokwerk;
|
||||
extern crate core;
|
||||
#[macro_use] extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
extern crate futures;
|
||||
extern crate hex;
|
||||
extern crate openssl;
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate rand;
|
||||
extern crate reqwest;
|
||||
extern crate rpki;
|
||||
#[macro_use] extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate syslog;
|
||||
extern crate tokio;
|
||||
@@ -25,21 +28,18 @@ extern crate toml;
|
||||
extern crate uuid;
|
||||
extern crate xml as xmlrs;
|
||||
|
||||
extern crate krill_client;
|
||||
extern crate krill_commons;
|
||||
extern crate krill_pubc;
|
||||
extern crate krill_pubd;
|
||||
extern crate krill_client;
|
||||
|
||||
|
||||
pub mod ca;
|
||||
pub mod auth;
|
||||
pub mod ca;
|
||||
pub mod config;
|
||||
pub mod endpoints;
|
||||
pub mod krillserver;
|
||||
pub mod http;
|
||||
pub mod test;
|
||||
pub mod krillserver;
|
||||
pub mod scheduler;
|
||||
pub mod test;
|
||||
|
||||
mod mq;
|
||||
|
||||
|
||||
|
||||
+14
-28
@@ -7,20 +7,11 @@ use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
ParentCaContact
|
||||
};
|
||||
use krill_commons::api::admin::{Handle, ParentCaContact};
|
||||
use krill_commons::api::ca::PublicationDelta;
|
||||
use krill_commons::eventsourcing;
|
||||
|
||||
use crate::ca::{
|
||||
Signer,
|
||||
Evt,
|
||||
EvtDet,
|
||||
CertAuth,
|
||||
ParentHandle
|
||||
};
|
||||
use crate::ca::{CertAuth, Evt, EvtDet, ParentHandle, Signer};
|
||||
|
||||
//------------ QueueEvent ----------------------------------------------------
|
||||
|
||||
@@ -35,12 +26,14 @@ pub enum QueueEvent {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventQueueListener {
|
||||
q: RwLock<Box<EventQueueStore>>
|
||||
q: RwLock<Box<EventQueueStore>>,
|
||||
}
|
||||
|
||||
impl EventQueueListener {
|
||||
pub fn in_mem() -> Self {
|
||||
EventQueueListener { q: RwLock::new(Box::new(MemoryEventQueue::new()))}
|
||||
EventQueueListener {
|
||||
q: RwLock::new(Box::new(MemoryEventQueue::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +51,9 @@ impl EventQueueListener {
|
||||
unsafe impl Send for EventQueueListener {}
|
||||
unsafe impl Sync for EventQueueListener {}
|
||||
|
||||
|
||||
/// Implement listening for CertAuth Published events.
|
||||
impl<S: Signer> eventsourcing::EventListener<CertAuth<S>> for EventQueueListener {
|
||||
fn listen(&self, _ca: &CertAuth<S>, event: &Evt) {
|
||||
|
||||
use krill_commons::eventsourcing::Event;
|
||||
|
||||
let json = serde_json::to_string_pretty(&event).unwrap();
|
||||
@@ -73,19 +64,15 @@ impl<S: Signer> eventsourcing::EventListener<CertAuth<S>> for EventQueueListener
|
||||
EvtDet::Published(_, _, _, delta) => {
|
||||
let evt = QueueEvent::Delta(handle.clone(), delta.clone());
|
||||
self.push_back(evt);
|
||||
},
|
||||
}
|
||||
EvtDet::TaPublished(delta) => {
|
||||
let evt = QueueEvent::Delta(handle.clone(), delta.clone());
|
||||
self.push_back(evt);
|
||||
},
|
||||
}
|
||||
EvtDet::ParentAdded(parent, contact) => {
|
||||
let evt = QueueEvent::ParentAdded(
|
||||
handle.clone(),
|
||||
parent.clone(),
|
||||
contact.clone()
|
||||
);
|
||||
let evt = QueueEvent::ParentAdded(handle.clone(), parent.clone(), contact.clone());
|
||||
self.push_back(evt);
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -103,18 +90,19 @@ trait EventQueueStore: fmt::Debug {
|
||||
fn push_back(&self, evt: QueueEvent);
|
||||
}
|
||||
|
||||
|
||||
//------------ MemoryEventQueue ----------------------------------------------
|
||||
|
||||
/// In memory event queue implementation.
|
||||
#[derive(Debug)]
|
||||
struct MemoryEventQueue {
|
||||
q: RwLock<VecDeque<QueueEvent>>
|
||||
q: RwLock<VecDeque<QueueEvent>>,
|
||||
}
|
||||
|
||||
impl MemoryEventQueue {
|
||||
pub fn new() -> Self {
|
||||
MemoryEventQueue { q: RwLock::new(VecDeque::new())}
|
||||
MemoryEventQueue {
|
||||
q: RwLock::new(VecDeque::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,5 +115,3 @@ impl EventQueueStore for MemoryEventQueue {
|
||||
self.q.write().unwrap().push_back(evt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+10
-19
@@ -19,21 +19,21 @@ pub struct Scheduler {
|
||||
event_sh: ScheduleHandle,
|
||||
|
||||
#[allow(dead_code)] // just need to keep this in scope
|
||||
republish_sh: ScheduleHandle
|
||||
republish_sh: ScheduleHandle,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn build(
|
||||
event_queue: Arc<EventQueueListener>,
|
||||
caserver: Arc<CaServer<OpenSslSigner>>,
|
||||
pubserver: Arc<PubServer>
|
||||
pubserver: Arc<PubServer>,
|
||||
) -> Self {
|
||||
|
||||
let event_sh = make_event_sh(event_queue, caserver.clone(), pubserver);
|
||||
let republish_sh = make_republish_sh(caserver);
|
||||
|
||||
Scheduler {
|
||||
event_sh, republish_sh
|
||||
event_sh,
|
||||
republish_sh,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ impl Scheduler {
|
||||
fn make_event_sh(
|
||||
event_queue: Arc<EventQueueListener>,
|
||||
caserver: Arc<CaServer<OpenSslSigner>>,
|
||||
pubserver: Arc<PubServer>
|
||||
pubserver: Arc<PubServer>,
|
||||
) -> ScheduleHandle {
|
||||
let mut scheduler = clokwerk::Scheduler::new();
|
||||
scheduler.every(1.seconds()).run(move || {
|
||||
@@ -49,21 +49,18 @@ fn make_event_sh(
|
||||
match evt {
|
||||
QueueEvent::Delta(handle, delta) => {
|
||||
publish(&handle, delta, &pubserver);
|
||||
},
|
||||
}
|
||||
QueueEvent::ParentAdded(handle, parent, contact) => {
|
||||
if let Err(e) = caserver.get_updates_from_parent(
|
||||
&handle, &parent, contact
|
||||
) {
|
||||
if let Err(e) = caserver.get_updates_from_parent(&handle, &parent, contact) {
|
||||
error!("Getting updates for {}, error: {}", &handle, e);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
scheduler.watch_thread(Duration::from_millis(100))
|
||||
}
|
||||
|
||||
|
||||
fn make_republish_sh(caserver: Arc<CaServer<OpenSslSigner>>) -> ScheduleHandle {
|
||||
let mut scheduler = clokwerk::Scheduler::new();
|
||||
scheduler.every(1.hours()).run(move || {
|
||||
@@ -75,16 +72,10 @@ fn make_republish_sh(caserver: Arc<CaServer<OpenSslSigner>>) -> ScheduleHandle {
|
||||
scheduler.watch_thread(Duration::from_millis(100))
|
||||
}
|
||||
|
||||
|
||||
fn publish(
|
||||
handle: &Handle,
|
||||
delta: PublicationDelta,
|
||||
pubserver: &PubServer
|
||||
) {
|
||||
fn publish(handle: &Handle, delta: PublicationDelta, pubserver: &PubServer) {
|
||||
debug!("Triggered publishing for CA: {}", handle);
|
||||
match pubserver.publish(handle, delta.into()) {
|
||||
Ok(()) => debug!("Published for CA: {}", handle),
|
||||
Err(e) => error!("Failed to publish for CA: {}, error: {}", handle, e)
|
||||
Err(e) => error!("Failed to publish for CA: {}, error: {}", handle, e),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-15
@@ -1,18 +1,21 @@
|
||||
//! Support for tests in other modules using a running krill server
|
||||
|
||||
use std::{thread, time};
|
||||
use std::path::PathBuf;
|
||||
use std::{thread, time};
|
||||
|
||||
use krill_commons::util::test;
|
||||
use krill_client::KrillClient;
|
||||
use krill_client::Error;
|
||||
use krill_client::options::{Command, Options};
|
||||
use krill_client::report::{ApiResponse, ReportFormat};
|
||||
use krill_client::Error;
|
||||
use krill_client::KrillClient;
|
||||
use krill_commons::util::test;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::http::server;
|
||||
|
||||
pub fn test_with_krill_server<F>(op: F) where F: FnOnce(PathBuf) -> () {
|
||||
pub fn test_with_krill_server<F>(op: F)
|
||||
where
|
||||
F: FnOnce(PathBuf) -> (),
|
||||
{
|
||||
test::test_under_tmp(|dir| {
|
||||
// Set up a test PubServer Config
|
||||
let server_conf = {
|
||||
@@ -22,13 +25,13 @@ pub fn test_with_krill_server<F>(op: F) where F: FnOnce(PathBuf) -> () {
|
||||
};
|
||||
|
||||
// Start the server
|
||||
thread::spawn(move || { server::start(&server_conf).unwrap() });
|
||||
thread::spawn(move || server::start(&server_conf).unwrap());
|
||||
|
||||
let mut tries = 0;
|
||||
loop {
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
if let Ok(_res) = health_check() {
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
tries += 1;
|
||||
@@ -37,7 +40,6 @@ pub fn test_with_krill_server<F>(op: F) where F: FnOnce(PathBuf) -> () {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
op(dir)
|
||||
})
|
||||
}
|
||||
@@ -51,24 +53,21 @@ fn health_check() -> Result<ApiResponse, Error> {
|
||||
test::https("https://localhost:3000/"),
|
||||
"secret",
|
||||
ReportFormat::Default,
|
||||
Command::Health
|
||||
Command::Health,
|
||||
);
|
||||
|
||||
KrillClient::process(krillc_opts)
|
||||
}
|
||||
|
||||
|
||||
pub fn krill_admin(command: Command) -> ApiResponse {
|
||||
let krillc_opts = Options::new(
|
||||
test::https("https://localhost:3000/"),
|
||||
"secret",
|
||||
ReportFormat::Json,
|
||||
command
|
||||
command,
|
||||
);
|
||||
match KrillClient::process(krillc_opts) {
|
||||
Ok(res) => res, // ok
|
||||
Err(e) => {
|
||||
panic!("{}", e)
|
||||
}
|
||||
Err(e) => panic!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,36 @@
|
||||
extern crate krill_commons;
|
||||
extern crate krill_client;
|
||||
extern crate krill_commons;
|
||||
extern crate krill_daemon;
|
||||
|
||||
use krill_client::options::{AddPublisher, Command, PublishersCommand};
|
||||
use krill_client::report::ApiResponse;
|
||||
use krill_commons::api::admin::{Handle, Token};
|
||||
use krill_commons::util::test;
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
Token
|
||||
};
|
||||
use krill_daemon::test::{test_with_krill_server, krill_admin};
|
||||
use krill_daemon::test::{krill_admin, test_with_krill_server};
|
||||
|
||||
fn add_publisher(handle: &str, base_uri: &str, token: &str) {
|
||||
let command = Command::Publishers(PublishersCommand::Add(
|
||||
AddPublisher {
|
||||
handle: Handle::from(handle),
|
||||
base_uri: test::rsync(base_uri),
|
||||
token: Token::from(token)
|
||||
}
|
||||
));
|
||||
let command = Command::Publishers(PublishersCommand::Add(AddPublisher {
|
||||
handle: Handle::from(handle),
|
||||
base_uri: test::rsync(base_uri),
|
||||
token: Token::from(token),
|
||||
}));
|
||||
krill_admin(command);
|
||||
}
|
||||
|
||||
fn deactivate_publisher(handle: &str) {
|
||||
let command = Command::Publishers(
|
||||
PublishersCommand::Deactivate(handle.to_string())
|
||||
);
|
||||
let command = Command::Publishers(PublishersCommand::Deactivate(handle.to_string()));
|
||||
|
||||
krill_admin(command);
|
||||
}
|
||||
|
||||
fn list_publishers() -> ApiResponse {
|
||||
let command = Command::Publishers(
|
||||
PublishersCommand::List
|
||||
);
|
||||
let command = Command::Publishers(PublishersCommand::List);
|
||||
|
||||
krill_admin(command)
|
||||
}
|
||||
|
||||
fn details_publisher(handle: &str) -> ApiResponse {
|
||||
let command = Command::Publishers(
|
||||
PublishersCommand::Details(handle.to_string())
|
||||
);
|
||||
let command = Command::Publishers(PublishersCommand::Details(handle.to_string()));
|
||||
|
||||
krill_admin(command)
|
||||
}
|
||||
@@ -49,7 +38,6 @@ fn details_publisher(handle: &str) -> ApiResponse {
|
||||
#[test]
|
||||
fn admin_publishers() {
|
||||
test_with_krill_server(|_d| {
|
||||
|
||||
let handle = "alice";
|
||||
let token = "secret";
|
||||
let base_rsync_uri_alice = "rsync://localhost/repo/alice/";
|
||||
@@ -65,8 +53,8 @@ fn admin_publishers() {
|
||||
assert_eq!(1, list.publishers().len());
|
||||
let alice = &list.publishers().get(0).unwrap();
|
||||
assert_eq!("alice", alice.id());
|
||||
},
|
||||
_ => panic!("Expected publisher list")
|
||||
}
|
||||
_ => panic!("Expected publisher list"),
|
||||
}
|
||||
|
||||
// Find details for alice
|
||||
@@ -75,8 +63,8 @@ fn admin_publishers() {
|
||||
ApiResponse::PublisherDetails(details) => {
|
||||
assert_eq!("alice", details.handle());
|
||||
assert_eq!(false, details.deactivated());
|
||||
},
|
||||
_ => panic!("Expected details")
|
||||
}
|
||||
_ => panic!("Expected details"),
|
||||
}
|
||||
|
||||
// Remove alice
|
||||
@@ -88,10 +76,8 @@ fn admin_publishers() {
|
||||
ApiResponse::PublisherDetails(details) => {
|
||||
assert_eq!("alice", details.handle());
|
||||
assert_eq!(true, details.deactivated());
|
||||
},
|
||||
_ => panic!("Expected details")
|
||||
}
|
||||
_ => panic!("Expected details"),
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+31
-55
@@ -1,93 +1,82 @@
|
||||
extern crate krill_daemon;
|
||||
extern crate krill_client;
|
||||
extern crate krill_commons;
|
||||
extern crate krill_daemon;
|
||||
extern crate krill_pubc;
|
||||
|
||||
use krill_client::options::{
|
||||
CaCommand,
|
||||
Command,
|
||||
TrustAnchorCommand,
|
||||
};
|
||||
use krill_client::options::{CaCommand, Command, TrustAnchorCommand};
|
||||
use krill_client::report::ApiResponse;
|
||||
use krill_commons::api::ca::{ResourceSet, CertAuthInfo, CaParentsInfo};
|
||||
use krill_commons::api::admin::{AddChildRequest, CertAuthInit, CertAuthPubMode, Handle, ParentCaContact, AddParentRequest, Token, ChildAuthRequest};
|
||||
use krill_commons::api::admin::{
|
||||
AddChildRequest, AddParentRequest, CertAuthInit, CertAuthPubMode, ChildAuthRequest, Handle,
|
||||
ParentCaContact, Token,
|
||||
};
|
||||
use krill_commons::api::ca::{CaParentsInfo, CertAuthInfo, ResourceSet};
|
||||
use krill_commons::remote::rfc8183;
|
||||
use krill_daemon::ca::ta_handle;
|
||||
use krill_daemon::test::{test_with_krill_server, krill_admin, wait_seconds};
|
||||
|
||||
use krill_daemon::test::{krill_admin, test_with_krill_server, wait_seconds};
|
||||
|
||||
fn init_ta() {
|
||||
krill_admin(Command::TrustAnchor(TrustAnchorCommand::Init));
|
||||
}
|
||||
|
||||
fn init_child(handle: &Handle, token: &Token) {
|
||||
let init = CertAuthInit::new(
|
||||
handle.clone(), token.clone(), CertAuthPubMode::Embedded
|
||||
);
|
||||
let init = CertAuthInit::new(handle.clone(), token.clone(), CertAuthPubMode::Embedded);
|
||||
krill_admin(Command::CertAuth(CaCommand::Init(init)));
|
||||
}
|
||||
|
||||
fn child_request(handle: &Handle) -> rfc8183::ChildRequest {
|
||||
match krill_admin(
|
||||
Command::CertAuth(CaCommand::ChildRequest(handle.clone()))
|
||||
) {
|
||||
match krill_admin(Command::CertAuth(CaCommand::ChildRequest(handle.clone()))) {
|
||||
ApiResponse::Rfc8183ChildRequest(req) => req,
|
||||
_ => panic!("Expected child request")
|
||||
_ => panic!("Expected child request"),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn add_child_to_ta_embedded(
|
||||
handle: &Handle,
|
||||
token: &Token,
|
||||
resources: ResourceSet
|
||||
resources: ResourceSet,
|
||||
) -> ParentCaContact {
|
||||
let auth = ChildAuthRequest::Embedded(token.clone());
|
||||
let req = AddChildRequest::new(handle.clone(), resources, auth);
|
||||
let res = krill_admin(
|
||||
Command::TrustAnchor(TrustAnchorCommand::AddChild(req))
|
||||
);
|
||||
let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req)));
|
||||
|
||||
match res {
|
||||
ApiResponse::ParentCaInfo(info) => info,
|
||||
_ => panic!("Expected ParentCaInfo response")
|
||||
_ => panic!("Expected ParentCaInfo response"),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_child_to_ta_rfc6492(
|
||||
handle: &Handle,
|
||||
req: rfc8183::ChildRequest,
|
||||
resources: ResourceSet
|
||||
resources: ResourceSet,
|
||||
) -> ParentCaContact {
|
||||
let auth = ChildAuthRequest::Rfc8183(req);
|
||||
let req = AddChildRequest::new(handle.clone(), resources, auth);
|
||||
let res = krill_admin(
|
||||
Command::TrustAnchor(TrustAnchorCommand::AddChild(req))
|
||||
);
|
||||
let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req)));
|
||||
|
||||
match res {
|
||||
ApiResponse::ParentCaInfo(info) => info,
|
||||
_ => panic!("Expected ParentCaInfo response")
|
||||
_ => panic!("Expected ParentCaInfo response"),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_parent_to_ca(handle: &Handle, parent: AddParentRequest) {
|
||||
krill_admin(
|
||||
Command::CertAuth(CaCommand::AddParent(handle.clone(), parent))
|
||||
);
|
||||
krill_admin(Command::CertAuth(CaCommand::AddParent(
|
||||
handle.clone(),
|
||||
parent,
|
||||
)));
|
||||
}
|
||||
|
||||
fn ca_details(handle: &Handle) -> CertAuthInfo {
|
||||
match krill_admin(Command::CertAuth(CaCommand::Show(handle.clone()))) {
|
||||
ApiResponse::CertAuthInfo(inf) => inf,
|
||||
_ => panic!("Expected cert auth info")
|
||||
_ => panic!("Expected cert auth info"),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) {
|
||||
let tries = 30;
|
||||
for counter in 1..tries+1 {
|
||||
for counter in 1..tries + 1 {
|
||||
if counter == tries {
|
||||
panic!("cms child did not get its resource certificate");
|
||||
}
|
||||
@@ -99,7 +88,7 @@ fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) {
|
||||
if let Some(rc) = parent.resources().get("all") {
|
||||
if let Some(key) = rc.current_key() {
|
||||
assert_eq!(resources, key.resources());
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,29 +98,21 @@ fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn ca_under_ta() {
|
||||
test_with_krill_server(|_d|{
|
||||
|
||||
test_with_krill_server(|_d| {
|
||||
let ta_handle = ta_handle();
|
||||
init_ta();
|
||||
|
||||
let emb_child_handle = Handle::from("child");
|
||||
let emb_child_token = Token::from("child");
|
||||
let emb_child_resources = ResourceSet::from_strs(
|
||||
"",
|
||||
"192.168.0.0/16",
|
||||
""
|
||||
).unwrap();
|
||||
let emb_child_resources = ResourceSet::from_strs("", "192.168.0.0/16", "").unwrap();
|
||||
|
||||
init_child(&emb_child_handle, &emb_child_token);
|
||||
|
||||
|
||||
let parent = {
|
||||
let parent_contact = add_child_to_ta_embedded(
|
||||
&emb_child_handle, &emb_child_token, emb_child_resources
|
||||
);
|
||||
let parent_contact =
|
||||
add_child_to_ta_embedded(&emb_child_handle, &emb_child_token, emb_child_resources);
|
||||
AddParentRequest::new(ta_handle.clone(), parent_contact)
|
||||
};
|
||||
|
||||
@@ -139,11 +120,7 @@ fn ca_under_ta() {
|
||||
|
||||
let cms_child_handle = Handle::from("rfc6492");
|
||||
let cms_child_token = Token::from("rfc6492");
|
||||
let cms_child_resources = ResourceSet::from_strs(
|
||||
"",
|
||||
"10.0.0.0/16",
|
||||
""
|
||||
).unwrap();
|
||||
let cms_child_resources = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap();
|
||||
|
||||
init_child(&cms_child_handle, &cms_child_token);
|
||||
let req = child_request(&cms_child_handle);
|
||||
@@ -151,9 +128,8 @@ fn ca_under_ta() {
|
||||
eprintln!("Child Request: {}", req);
|
||||
|
||||
let parent = {
|
||||
let contact = add_child_to_ta_rfc6492(
|
||||
&cms_child_handle, req, cms_child_resources.clone()
|
||||
);
|
||||
let contact =
|
||||
add_child_to_ta_rfc6492(&cms_child_handle, req, cms_child_resources.clone());
|
||||
AddParentRequest::new(ta_handle.clone(), contact)
|
||||
};
|
||||
|
||||
|
||||
@@ -3,32 +3,24 @@ extern crate krill_commons;
|
||||
extern crate krill_daemon;
|
||||
extern crate krill_pubc;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use krill_client::KrillClient;
|
||||
use krill_client::options::{
|
||||
AddPublisher,
|
||||
AddRfc8181Client,
|
||||
Command,
|
||||
Options,
|
||||
PublishersCommand,
|
||||
Rfc8181Command,
|
||||
AddPublisher, AddRfc8181Client, Command, Options, PublishersCommand, Rfc8181Command,
|
||||
};
|
||||
use krill_client::report::ReportFormat;
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
Token
|
||||
};
|
||||
use krill_client::KrillClient;
|
||||
use krill_commons::api::admin::{Handle, Token};
|
||||
use krill_commons::api::publication::ListReply;
|
||||
use krill_commons::remote::rfc8183::RepositoryResponse;
|
||||
use krill_commons::util::file::CurrentFile;
|
||||
use krill_commons::util::file;
|
||||
use krill_commons::util::file::CurrentFile;
|
||||
use krill_commons::util::httpclient;
|
||||
use krill_commons::util::test;
|
||||
use krill_pubc::{ApiResponse, Format};
|
||||
use krill_pubc::apiclient;
|
||||
use krill_pubc::cmsclient;
|
||||
use krill_pubc::cmsclient::PubClient;
|
||||
use krill_pubc::{ApiResponse, Format};
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn list(server_uri: &str, handle: &str, token: &str) -> apiclient::Options {
|
||||
let conn = apiclient::Connection::build(server_uri, handle, token).unwrap();
|
||||
@@ -43,7 +35,7 @@ fn sync(
|
||||
handle: &str,
|
||||
token: &str,
|
||||
syncdir: &PathBuf,
|
||||
base_uri: &str
|
||||
base_uri: &str,
|
||||
) -> apiclient::Options {
|
||||
let conn = apiclient::Connection::build(server_uri, handle, token).unwrap();
|
||||
let cmd = apiclient::Command::sync(syncdir.to_str().unwrap(), base_uri).unwrap();
|
||||
@@ -57,31 +49,25 @@ fn execute_krillc_command(command: Command) {
|
||||
test::https("https://localhost:3000/"),
|
||||
"secret",
|
||||
ReportFormat::Default,
|
||||
command
|
||||
command,
|
||||
);
|
||||
match KrillClient::process(krillc_opts) {
|
||||
Ok(_res) => {}, // ok
|
||||
Err(e) => {
|
||||
panic!("{}", e)
|
||||
}
|
||||
Ok(_res) => {} // ok
|
||||
Err(e) => panic!("{}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_publisher(handle: &str, base_uri: &str, token: &str) {
|
||||
let command = Command::Publishers(PublishersCommand::Add(
|
||||
AddPublisher {
|
||||
handle: Handle::from(handle),
|
||||
base_uri: test::rsync(base_uri),
|
||||
token: Token::from(token)
|
||||
}
|
||||
));
|
||||
let command = Command::Publishers(PublishersCommand::Add(AddPublisher {
|
||||
handle: Handle::from(handle),
|
||||
base_uri: test::rsync(base_uri),
|
||||
token: Token::from(token),
|
||||
}));
|
||||
execute_krillc_command(command);
|
||||
}
|
||||
|
||||
fn remove_publisher(handle: &str) {
|
||||
let command = Command::Publishers(
|
||||
PublishersCommand::Deactivate(handle.to_string())
|
||||
);
|
||||
let command = Command::Publishers(PublishersCommand::Deactivate(handle.to_string()));
|
||||
|
||||
execute_krillc_command(command);
|
||||
}
|
||||
@@ -91,18 +77,14 @@ fn rfc8181_client_init(handle: &str, state_dir: &PathBuf) {
|
||||
rfc8181_client_process_command(command, &state_dir);
|
||||
}
|
||||
|
||||
fn rfc8181_client_add(state_dir: &PathBuf) {
|
||||
fn rfc8181_client_add(state_dir: &PathBuf) {
|
||||
let mut pr_path = state_dir.clone();
|
||||
pr_path.push("request.xml");
|
||||
|
||||
let command = cmsclient::Command::publisher_request(pr_path.clone());
|
||||
rfc8181_client_process_command(command, &state_dir);
|
||||
|
||||
let command = Command::Rfc8181(
|
||||
Rfc8181Command::Add(
|
||||
AddRfc8181Client { xml: pr_path }
|
||||
)
|
||||
);
|
||||
let command = Command::Rfc8181(Rfc8181Command::Add(AddRfc8181Client { xml: pr_path }));
|
||||
|
||||
execute_krillc_command(command);
|
||||
}
|
||||
@@ -119,7 +101,7 @@ fn rfc8181_client_list(state_dir: &PathBuf) -> ListReply {
|
||||
let api_response = rfc8181_client_process_command(command, &state_dir);
|
||||
match api_response {
|
||||
ApiResponse::Success => panic!("Expected list"),
|
||||
ApiResponse::List(list) => list
|
||||
ApiResponse::List(list) => list,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,13 +119,14 @@ fn rfc8181_client_process_command(command: cmsclient::Command, state_dir: &PathB
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn get_repository_response(handle: &str) -> RepositoryResponse {
|
||||
let uri = format!("https://localhost:3000/api/v1/rfc8181/{}/response.xml", handle);
|
||||
let uri = format!(
|
||||
"https://localhost:3000/api/v1/rfc8181/{}/response.xml",
|
||||
handle
|
||||
);
|
||||
let content_type = "application/xml";
|
||||
let token = Token::from("secret");
|
||||
|
||||
let xml = httpclient::get_text(
|
||||
&uri, content_type, Some(&token)
|
||||
).unwrap();
|
||||
let xml = httpclient::get_text(&uri, content_type, Some(&token)).unwrap();
|
||||
|
||||
RepositoryResponse::validate(xml.as_bytes()).unwrap()
|
||||
}
|
||||
@@ -151,7 +134,6 @@ fn get_repository_response(handle: &str) -> RepositoryResponse {
|
||||
#[test]
|
||||
fn client_publish() {
|
||||
krill_daemon::test::test_with_krill_server(|d| {
|
||||
|
||||
let server_uri = "https://localhost:3000/";
|
||||
let handle = "alice";
|
||||
let token = "secret";
|
||||
@@ -163,57 +145,46 @@ fn client_publish() {
|
||||
|
||||
// Calls to api should require the correct token
|
||||
{
|
||||
let res = apiclient::execute(list(
|
||||
server_uri,
|
||||
handle,
|
||||
"wrong token"
|
||||
));
|
||||
let res = apiclient::execute(list(server_uri, handle, "wrong token"));
|
||||
|
||||
match res {
|
||||
Err(apiclient::Error::HttpClientError
|
||||
(httpclient::Error::Forbidden)) => {},
|
||||
Err(apiclient::Error::HttpClientError(httpclient::Error::Forbidden)) => {}
|
||||
Err(e) => panic!("Expected forbidden, got: {}", e),
|
||||
_ => panic!("Expected forbidden")
|
||||
_ => panic!("Expected forbidden"),
|
||||
}
|
||||
}
|
||||
|
||||
// List files at server, expect no files
|
||||
{
|
||||
let list = apiclient::execute(list(
|
||||
server_uri,
|
||||
handle,
|
||||
token
|
||||
)).unwrap();
|
||||
let list = apiclient::execute(list(server_uri, handle, token)).unwrap();
|
||||
|
||||
match list {
|
||||
ApiResponse::List(list) => {
|
||||
assert_eq!(0, list.elements().len());
|
||||
},
|
||||
_ => panic!("Expected list")
|
||||
}
|
||||
_ => panic!("Expected list"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create files on disk to sync
|
||||
let sync_dir = test::sub_dir(&d);
|
||||
let file_a = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/a.txt"),
|
||||
&test::as_bytes("a")
|
||||
&test::as_bytes("a"),
|
||||
);
|
||||
let file_b = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/b.txt"),
|
||||
&test::as_bytes("b")
|
||||
&test::as_bytes("b"),
|
||||
);
|
||||
let file_c = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/c.txt"),
|
||||
&test::as_bytes("c")
|
||||
&test::as_bytes("c"),
|
||||
);
|
||||
|
||||
file::save_in_dir(&file_a.to_bytes(), &sync_dir, "a.txt").unwrap();
|
||||
file::save_in_dir(&file_b.to_bytes(), &sync_dir, "b.txt").unwrap();
|
||||
file::save_in_dir(&file_c.to_bytes(), &sync_dir, "c.txt").unwrap();
|
||||
|
||||
|
||||
// Must refuse syncing files outside of publisher base dir
|
||||
{
|
||||
let api_res = apiclient::execute(sync(
|
||||
@@ -221,13 +192,12 @@ fn client_publish() {
|
||||
handle,
|
||||
token,
|
||||
&sync_dir,
|
||||
base_rsync_uri_bob
|
||||
base_rsync_uri_bob,
|
||||
));
|
||||
|
||||
assert!(api_res.is_err())
|
||||
}
|
||||
|
||||
|
||||
// Sync files
|
||||
{
|
||||
let api_res = apiclient::execute(sync(
|
||||
@@ -235,45 +205,37 @@ fn client_publish() {
|
||||
handle,
|
||||
token,
|
||||
&sync_dir,
|
||||
base_rsync_uri_alice
|
||||
)).unwrap();
|
||||
base_rsync_uri_alice,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ApiResponse::Success, api_res);
|
||||
}
|
||||
|
||||
// We should now see these files when we list
|
||||
{
|
||||
let list = apiclient::execute(list(
|
||||
server_uri,
|
||||
handle,
|
||||
token
|
||||
)).unwrap();
|
||||
let list = apiclient::execute(list(server_uri, handle, token)).unwrap();
|
||||
|
||||
match list {
|
||||
ApiResponse::List(list) => {
|
||||
assert_eq!(3, list.elements().len());
|
||||
|
||||
let returned_set: HashSet<_> = list.elements().iter().collect();
|
||||
let returned_set: HashSet<_> = list.elements().iter().collect();
|
||||
|
||||
let list_el_a = file_a.into_list_element();
|
||||
let list_el_b = file_b.into_list_element();
|
||||
let list_el_c = file_c.clone().into_list_element();
|
||||
|
||||
let expected_elements = vec![
|
||||
&list_el_a,
|
||||
&list_el_b,
|
||||
&list_el_c
|
||||
];
|
||||
let expected_elements = vec![&list_el_a, &list_el_b, &list_el_c];
|
||||
let expected_set: HashSet<_> = expected_elements.into_iter().collect();
|
||||
assert_eq!(expected_set, returned_set);
|
||||
},
|
||||
_ => panic!("Expected list")
|
||||
}
|
||||
_ => panic!("Expected list"),
|
||||
}
|
||||
}
|
||||
|
||||
// XXX TODO We should also see these files in RRDP
|
||||
|
||||
|
||||
// Now we should be able to delete it all again
|
||||
file::delete_in_dir(&sync_dir, "a.txt").unwrap();
|
||||
file::delete_in_dir(&sync_dir, "b.txt").unwrap();
|
||||
@@ -285,33 +247,30 @@ fn client_publish() {
|
||||
handle,
|
||||
token,
|
||||
&sync_dir,
|
||||
base_rsync_uri_alice
|
||||
)).unwrap();
|
||||
base_rsync_uri_alice,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ApiResponse::Success, api_res);
|
||||
}
|
||||
|
||||
// List files at server, expect 1 file (c.txt)
|
||||
{
|
||||
let list = apiclient::execute(list(
|
||||
server_uri,
|
||||
handle,
|
||||
token
|
||||
)).unwrap();
|
||||
let list = apiclient::execute(list(server_uri, handle, token)).unwrap();
|
||||
|
||||
match list {
|
||||
ApiResponse::List(list) => {
|
||||
assert_eq!(1, list.elements().len());
|
||||
|
||||
let returned_set: HashSet<_> = list.elements().iter().collect();
|
||||
let returned_set: HashSet<_> = list.elements().iter().collect();
|
||||
|
||||
let list_el_c = file_c.into_list_element();
|
||||
|
||||
let expected_elements = vec![&list_el_c];
|
||||
let expected_set: HashSet<_> = expected_elements.into_iter().collect();
|
||||
assert_eq!(expected_set, returned_set);
|
||||
},
|
||||
_ => panic!("Expected list")
|
||||
}
|
||||
_ => panic!("Expected list"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,15 +313,15 @@ fn client_publish() {
|
||||
let sync_dir = test::sub_dir(&d);
|
||||
let file_a = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/a.txt"),
|
||||
&test::as_bytes("a")
|
||||
&test::as_bytes("a"),
|
||||
);
|
||||
let file_b = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/b.txt"),
|
||||
&test::as_bytes("b")
|
||||
&test::as_bytes("b"),
|
||||
);
|
||||
let file_c = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/c.txt"),
|
||||
&test::as_bytes("c")
|
||||
&test::as_bytes("c"),
|
||||
);
|
||||
|
||||
file::save_in_dir(&file_a.to_bytes(), &sync_dir, "a.txt").unwrap();
|
||||
@@ -376,5 +335,4 @@ fn client_publish() {
|
||||
let list = rfc8181_client_list(&state_dir);
|
||||
assert_eq!(3, list.elements().len());
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
extern crate krill_daemon;
|
||||
extern crate krill_client;
|
||||
extern crate krill_commons;
|
||||
extern crate krill_daemon;
|
||||
|
||||
use krill_daemon::test::{test_with_krill_server, krill_admin};
|
||||
use krill_client::options::{Command, TrustAnchorCommand};
|
||||
use krill_daemon::test::{krill_admin, test_with_krill_server};
|
||||
|
||||
#[test]
|
||||
fn embedded_trust_anchor() {
|
||||
test_with_krill_server(|_d|{
|
||||
|
||||
test_with_krill_server(|_d| {
|
||||
let command = Command::TrustAnchor(TrustAnchorCommand::Init);
|
||||
krill_admin(command);
|
||||
|
||||
let command = Command::TrustAnchor(TrustAnchorCommand::Show);
|
||||
krill_admin(command);
|
||||
|
||||
// let command = Command::TrustAnchor(TrustAnchorCommand::Publish);
|
||||
// let _res = execute_krillc_command(command);
|
||||
// let command = Command::TrustAnchor(TrustAnchorCommand::Publish);
|
||||
// let _res = execute_krillc_command(command);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+95
-96
@@ -7,7 +7,7 @@ use rpki::uri;
|
||||
|
||||
use krill_commons::api::admin::Token;
|
||||
use krill_commons::api::publication;
|
||||
use krill_commons::util::{httpclient, file};
|
||||
use krill_commons::util::{file, httpclient};
|
||||
|
||||
use crate::{create_delta, ApiResponse, Format};
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{create_delta, ApiResponse, Format};
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Command {
|
||||
List,
|
||||
Sync(PathBuf, uri::Rsync)
|
||||
Sync(PathBuf, uri::Rsync),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
@@ -25,7 +25,7 @@ impl Command {
|
||||
}
|
||||
pub fn sync(dir: &str, base_uri: &str) -> Result<Self, Error> {
|
||||
let dir = PathBuf::from(dir);
|
||||
if ! base_uri.ends_with('/') {
|
||||
if !base_uri.ends_with('/') {
|
||||
Err(Error::InvalidBaseUri)
|
||||
} else {
|
||||
let uri = uri::Rsync::from_str(base_uri)?;
|
||||
@@ -34,8 +34,6 @@ impl Command {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------ Connection ---------------------------------------------------
|
||||
|
||||
pub struct Connection {
|
||||
@@ -50,28 +48,26 @@ pub struct Connection {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn build(
|
||||
server_uri: &str,
|
||||
handle: &str,
|
||||
token: &str
|
||||
) -> Result<Self, Error> {
|
||||
pub fn build(server_uri: &str, handle: &str, token: &str) -> Result<Self, Error> {
|
||||
let server_uri = uri::Https::from_str(server_uri)?;
|
||||
let handle = handle.to_string();
|
||||
let token = Token::from(token);
|
||||
Ok(Connection {server_uri, handle, token })
|
||||
let handle = handle.to_string();
|
||||
let token = Token::from(token);
|
||||
Ok(Connection {
|
||||
server_uri,
|
||||
handle,
|
||||
token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Options ------------------------------------------------------
|
||||
|
||||
pub struct Options {
|
||||
connection: Connection,
|
||||
cmd: Command,
|
||||
format: Format
|
||||
format: Format,
|
||||
}
|
||||
|
||||
|
||||
impl Options {
|
||||
fn parts(self) -> (Connection, Command) {
|
||||
(self.connection, self.cmd)
|
||||
@@ -79,73 +75,81 @@ impl Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn new(
|
||||
connection: Connection,
|
||||
cmd: Command,
|
||||
format: Format
|
||||
) -> Self {
|
||||
Options { connection, cmd, format }
|
||||
pub fn new(connection: Connection, cmd: Command, format: Format) -> Self {
|
||||
Options {
|
||||
connection,
|
||||
cmd,
|
||||
format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(&self) -> &Format { &self.format }
|
||||
pub fn format(&self) -> &Format {
|
||||
&self.format
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Options {
|
||||
pub fn create() -> Result<Self, Error> {
|
||||
let m = App::new("NLnet Labs RRDP client (API)")
|
||||
.version("0.1b")
|
||||
.arg(Arg::with_name("server")
|
||||
.short("s")
|
||||
.long("server")
|
||||
.value_name("uri")
|
||||
.help("Base server uri.")
|
||||
.required(true)
|
||||
.arg(
|
||||
Arg::with_name("server")
|
||||
.short("s")
|
||||
.long("server")
|
||||
.value_name("uri")
|
||||
.help("Base server uri.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("name")
|
||||
.help("Handle by which this client is known to the server.")
|
||||
.required(true)
|
||||
.arg(
|
||||
Arg::with_name("handle")
|
||||
.short("h")
|
||||
.long("handle")
|
||||
.value_name("name")
|
||||
.help("Handle by which this client is known to the server.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(Arg::with_name("token")
|
||||
.short("t")
|
||||
.long("token")
|
||||
.value_name("passphrase")
|
||||
.help("Token for this particular client handle at the server")
|
||||
.required(true)
|
||||
.arg(
|
||||
Arg::with_name("token")
|
||||
.short("t")
|
||||
.long("token")
|
||||
.value_name("passphrase")
|
||||
.help("Token for this particular client handle at the server")
|
||||
.required(true),
|
||||
)
|
||||
.arg(Arg::with_name("format")
|
||||
.short("f")
|
||||
.long("format")
|
||||
.value_name("text|json|none")
|
||||
.help("Specify the output format. Defaults to 'text'.")
|
||||
.required(false)
|
||||
.arg(
|
||||
Arg::with_name("format")
|
||||
.short("f")
|
||||
.long("format")
|
||||
.value_name("text|json|none")
|
||||
.help("Specify the output format. Defaults to 'text'.")
|
||||
.required(false),
|
||||
)
|
||||
.subcommand(SubCommand::with_name("list"))
|
||||
.subcommand(SubCommand::with_name("sync")
|
||||
.arg(Arg::with_name("dir")
|
||||
.short("d")
|
||||
.long("dir")
|
||||
.value_name("directory")
|
||||
.help("Directory to synchronise.")
|
||||
.required(true)
|
||||
)
|
||||
.arg(Arg::with_name("rsync_base")
|
||||
.short("r")
|
||||
.long("rsync_base")
|
||||
.value_name("uri")
|
||||
.help("Base rsync URI (name space) for this dir.")
|
||||
.required(true)
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("sync")
|
||||
.arg(
|
||||
Arg::with_name("dir")
|
||||
.short("d")
|
||||
.long("dir")
|
||||
.value_name("directory")
|
||||
.help("Directory to synchronise.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rsync_base")
|
||||
.short("r")
|
||||
.long("rsync_base")
|
||||
.value_name("uri")
|
||||
.help("Base rsync URI (name space) for this dir.")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let connection = {
|
||||
let server_uri = m.value_of("server").unwrap();
|
||||
let handle = m.value_of("handle").unwrap();
|
||||
let token = m.value_of("token").unwrap();
|
||||
let handle = m.value_of("handle").unwrap();
|
||||
let token = m.value_of("token").unwrap();
|
||||
Connection::build(server_uri, handle, token)?
|
||||
};
|
||||
|
||||
@@ -157,7 +161,7 @@ impl Options {
|
||||
let rsync_uri = m.value_of("rsync_base").unwrap();
|
||||
Command::sync(dir, rsync_uri)?
|
||||
} else {
|
||||
return Err(Error::NoCommand)
|
||||
return Err(Error::NoCommand);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -174,16 +178,11 @@ pub fn execute(options: Options) -> Result<ApiResponse, Error> {
|
||||
let (connection, cmd) = options.parts();
|
||||
|
||||
match cmd {
|
||||
Command::List => {
|
||||
list_query(&connection).map(ApiResponse::List)
|
||||
},
|
||||
Command::Sync(dir, rsync_uri) => {
|
||||
sync(&connection, &dir, &rsync_uri)
|
||||
}
|
||||
Command::List => list_query(&connection).map(ApiResponse::List),
|
||||
Command::Sync(dir, rsync_uri) => sync(&connection, &dir, &rsync_uri),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn list_query(connection: &Connection) -> Result<publication::ListReply, Error> {
|
||||
let uri = format!(
|
||||
"{}publication/{}",
|
||||
@@ -191,31 +190,23 @@ fn list_query(connection: &Connection) -> Result<publication::ListReply, Error>
|
||||
&connection.handle
|
||||
);
|
||||
|
||||
match httpclient::get_json::<publication::ListReply>(
|
||||
&uri,
|
||||
Some(&connection.token)
|
||||
) {
|
||||
match httpclient::get_json::<publication::ListReply>(&uri, Some(&connection.token)) {
|
||||
Err(e) => Err(Error::HttpClientError(e)),
|
||||
Ok(list) => Ok(list)
|
||||
Ok(list) => Ok(list),
|
||||
}
|
||||
}
|
||||
|
||||
fn sync(
|
||||
connection: &Connection,
|
||||
dir: &PathBuf,
|
||||
base_rsync: &uri::Rsync
|
||||
base_rsync: &uri::Rsync,
|
||||
) -> Result<ApiResponse, Error> {
|
||||
let list_reply = list_query(connection)?;
|
||||
let delta = create_delta(
|
||||
&list_reply,
|
||||
dir,
|
||||
base_rsync
|
||||
)?;
|
||||
let delta = create_delta(&list_reply, dir, base_rsync)?;
|
||||
|
||||
let uri = format!(
|
||||
"{}publication/{}",
|
||||
&connection.server_uri,
|
||||
&connection.handle
|
||||
&connection.server_uri, &connection.handle
|
||||
);
|
||||
|
||||
httpclient::post_json(&uri, delta, Some(&connection.token))?;
|
||||
@@ -239,36 +230,44 @@ pub enum Error {
|
||||
#[display(fmt = "Expected a response body, but got nothing.")]
|
||||
NoResponse,
|
||||
|
||||
#[display(fmt="HTTP client error: {}", _0)]
|
||||
#[display(fmt = "HTTP client error: {}", _0)]
|
||||
HttpClientError(httpclient::Error),
|
||||
|
||||
#[display(fmt="Received invalid json response: {}", _0)]
|
||||
#[display(fmt = "Received invalid json response: {}", _0)]
|
||||
JsonError(serde_json::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
FileError(file::Error),
|
||||
|
||||
#[display(fmt="Unsupported output format. Use text, json or none.")]
|
||||
#[display(fmt = "Unsupported output format. Use text, json or none.")]
|
||||
UnsupportedOutputFormat,
|
||||
|
||||
#[display(fmt="Base URI must end with '/'.")]
|
||||
#[display(fmt = "Base URI must end with '/'.")]
|
||||
InvalidBaseUri,
|
||||
}
|
||||
|
||||
impl From<uri::Error> for Error {
|
||||
fn from(e: uri::Error) -> Self { Error::UriError(e) }
|
||||
fn from(e: uri::Error) -> Self {
|
||||
Error::UriError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(e: serde_json::Error) -> Self { Error::JsonError(e) }
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
Error::JsonError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<file::Error> for Error {
|
||||
fn from(e: file::Error) -> Self { Error::FileError(e) }
|
||||
fn from(e: file::Error) -> Self {
|
||||
Error::FileError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<httpclient::Error> for Error {
|
||||
fn from(e: httpclient::Error) -> Self { Error::HttpClientError(e) }
|
||||
fn from(e: httpclient::Error) -> Self {
|
||||
Error::HttpClientError(e)
|
||||
}
|
||||
}
|
||||
|
||||
// -- Tested in integration tests.
|
||||
// -- Tested in integration tests.
|
||||
|
||||
@@ -4,9 +4,8 @@ extern crate krill_pubc;
|
||||
use krill_pubc::apiclient;
|
||||
|
||||
fn main() {
|
||||
|
||||
let options = match apiclient::Options::create() {
|
||||
Ok(o) => o,
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing options: {}", e);
|
||||
::std::process::exit(1);
|
||||
@@ -22,5 +21,4 @@ fn main() {
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ use krill_pubc::cmsclient;
|
||||
use krill_pubc::cmsclient::PubClient;
|
||||
|
||||
fn main() {
|
||||
|
||||
let options = match cmsclient::Options::create() {
|
||||
Ok(o) => o,
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
::std::process::exit(1);
|
||||
@@ -23,5 +22,4 @@ fn main() {
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+125
-114
@@ -1,17 +1,17 @@
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use crate::{create_delta, ApiResponse, Format};
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use krill_commons::api::publication::ListReply;
|
||||
use krill_commons::util::{softsigner, file};
|
||||
use krill_commons::util::softsigner::OpenSslSigner;
|
||||
use krill_commons::remote::builder::IdCertBuilder;
|
||||
use krill_commons::remote::id::{MyIdentity, MyRepoInfo, ParentInfo};
|
||||
use krill_commons::remote::proxy::{ClientError, ClientProxy};
|
||||
use krill_commons::remote::rfc8183;
|
||||
use krill_commons::remote::rfc8183::RepositoryResponse;
|
||||
use krill_commons::remote::id::{MyIdentity, ParentInfo, MyRepoInfo};
|
||||
use krill_commons::remote::proxy::{ClientProxy, ClientError};
|
||||
use crate::{create_delta, ApiResponse, Format};
|
||||
use krill_commons::util::softsigner::OpenSslSigner;
|
||||
use krill_commons::util::{file, softsigner};
|
||||
use rpki::crypto::PublicKeyFormat;
|
||||
use rpki::crypto::Signer;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
||||
pub enum Command {
|
||||
@@ -19,7 +19,7 @@ pub enum Command {
|
||||
PublisherRequest(PathBuf),
|
||||
RepoResponse(PathBuf),
|
||||
List,
|
||||
Sync(PathBuf)
|
||||
Sync(PathBuf),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
@@ -44,9 +44,8 @@ impl Command {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct PubClient {
|
||||
state_dir: PathBuf
|
||||
state_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl PubClient {
|
||||
@@ -58,21 +57,21 @@ impl PubClient {
|
||||
let request = client.publisher_request()?;
|
||||
request.save(&path)?;
|
||||
Ok(ApiResponse::Success)
|
||||
},
|
||||
}
|
||||
Command::RepoResponse(path) => {
|
||||
let xml = file::read(&path)?;
|
||||
let response = RepositoryResponse::validate(xml.as_ref())?;
|
||||
client.process_repo_response(&response)?;
|
||||
Ok(ApiResponse::Success)
|
||||
},
|
||||
}
|
||||
Command::Init(name) => {
|
||||
client.init(&name)?;
|
||||
Ok(ApiResponse::Success)
|
||||
},
|
||||
}
|
||||
Command::List => {
|
||||
let reply = client.list()?;
|
||||
Ok(ApiResponse::List(reply))
|
||||
},
|
||||
}
|
||||
Command::Sync(dir) => {
|
||||
client.sync(&dir)?;
|
||||
Ok(ApiResponse::Success)
|
||||
@@ -81,7 +80,9 @@ impl PubClient {
|
||||
}
|
||||
|
||||
fn build(state_dir: &PathBuf) -> Result<Self, Error> {
|
||||
Ok(PubClient { state_dir: state_dir.clone() })
|
||||
Ok(PubClient {
|
||||
state_dir: state_dir.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Initialises a new publication client, using a new key pair, and
|
||||
@@ -98,31 +99,26 @@ impl PubClient {
|
||||
}
|
||||
|
||||
/// Makes a publisher request, which can presented as an RFC8183 xml.
|
||||
fn publisher_request(
|
||||
&mut self
|
||||
) -> Result<rfc8183::PublisherRequest, Error> {
|
||||
fn publisher_request(&mut self) -> Result<rfc8183::PublisherRequest, Error> {
|
||||
let id = self.my_identity()?;
|
||||
Ok(
|
||||
rfc8183::PublisherRequest::new(
|
||||
None,
|
||||
id.name(),
|
||||
id.id_cert().clone()
|
||||
)
|
||||
)
|
||||
Ok(rfc8183::PublisherRequest::new(
|
||||
None,
|
||||
id.name(),
|
||||
id.id_cert().clone(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Process the publication server parent response.
|
||||
fn process_repo_response(
|
||||
&mut self,
|
||||
response: &rfc8183::RepositoryResponse
|
||||
response: &rfc8183::RepositoryResponse,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
// Store parent info
|
||||
{
|
||||
let parent_info = ParentInfo::new(
|
||||
response.publisher_handle().clone(),
|
||||
response.id_cert().clone(),
|
||||
response.service_uri().clone()
|
||||
response.service_uri().clone(),
|
||||
);
|
||||
|
||||
file::save_json(&parent_info, &self.path_my_parent())?;
|
||||
@@ -132,7 +128,7 @@ impl PubClient {
|
||||
{
|
||||
let repo_info = MyRepoInfo::new(
|
||||
response.sia_base().clone(),
|
||||
response.rrdp_notification_uri().clone()
|
||||
response.rrdp_notification_uri().clone(),
|
||||
);
|
||||
|
||||
file::save_json(&repo_info, &self.path_my_repo())?;
|
||||
@@ -153,11 +149,7 @@ impl PubClient {
|
||||
let repo = self.my_repo()?;
|
||||
|
||||
let list_reply = self.list()?;
|
||||
let delta = create_delta(
|
||||
&list_reply,
|
||||
dir,
|
||||
repo.sia_base()
|
||||
)?;
|
||||
let delta = create_delta(&list_reply, dir, repo.sia_base())?;
|
||||
|
||||
proxy.delta(delta).map_err(Error::ClientError)
|
||||
}
|
||||
@@ -200,20 +192,23 @@ impl PubClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Options --------------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
state_dir: PathBuf,
|
||||
command: Command,
|
||||
format: Format
|
||||
format: Format,
|
||||
}
|
||||
|
||||
/// # Accessors
|
||||
impl Options {
|
||||
pub fn new(state_dir: PathBuf, command: Command, format: Format) -> Self {
|
||||
Options { state_dir, command, format }
|
||||
Options {
|
||||
state_dir,
|
||||
command,
|
||||
format,
|
||||
}
|
||||
}
|
||||
pub fn state_dir(&self) -> &PathBuf {
|
||||
&self.state_dir
|
||||
@@ -223,7 +218,9 @@ impl Options {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub fn format(&self) -> &Format { &self.format }
|
||||
pub fn format(&self) -> &Format {
|
||||
&self.format
|
||||
}
|
||||
}
|
||||
|
||||
/// # Create
|
||||
@@ -232,69 +229,82 @@ impl Options {
|
||||
pub fn create() -> Result<Self, OptionsError> {
|
||||
let m = App::new("NLnet Labs RRDP Client (RFC8181)")
|
||||
.version("0.1b")
|
||||
|
||||
.arg(Arg::with_name("state")
|
||||
.short("s")
|
||||
.long("state")
|
||||
.value_name("FILE")
|
||||
.help("Specify the directory where this publication client \
|
||||
maintains its state.")
|
||||
.required(true))
|
||||
|
||||
.arg(Arg::with_name("format")
|
||||
.short("f")
|
||||
.long("format")
|
||||
.value_name("text|json|none")
|
||||
.help("Specify the output format. Defaults to 'none'.")
|
||||
.required(false)
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("init")
|
||||
.about("(Re-)Initialise the identity certificate and key \
|
||||
pair.")
|
||||
.arg(Arg::with_name("name")
|
||||
.short("n")
|
||||
.long("name")
|
||||
.value_name("NAME")
|
||||
.help("Specify the name for this publication client.")
|
||||
.required(true))
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("request")
|
||||
.about("Generate the publisher request XML")
|
||||
.arg(Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.arg(
|
||||
Arg::with_name("state")
|
||||
.short("s")
|
||||
.long("state")
|
||||
.value_name("FILE")
|
||||
.help("The name of the file to write the request to.")
|
||||
.required(true))
|
||||
.help(
|
||||
"Specify the directory where this publication client \
|
||||
maintains its state.",
|
||||
)
|
||||
.required(true),
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("response")
|
||||
.about("Process the repository response XML")
|
||||
.arg(Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.value_name("FILE")
|
||||
.help("The name of the file containing the response.")
|
||||
.required(true))
|
||||
.arg(
|
||||
Arg::with_name("format")
|
||||
.short("f")
|
||||
.long("format")
|
||||
.value_name("text|json|none")
|
||||
.help("Specify the output format. Defaults to 'none'.")
|
||||
.required(false),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("init")
|
||||
.about(
|
||||
"(Re-)Initialise the identity certificate and key \
|
||||
pair.",
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("name")
|
||||
.short("n")
|
||||
.long("name")
|
||||
.value_name("NAME")
|
||||
.help("Specify the name for this publication client.")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("request")
|
||||
.about("Generate the publisher request XML")
|
||||
.arg(
|
||||
Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.value_name("FILE")
|
||||
.help("The name of the file to write the request to.")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("response")
|
||||
.about("Process the repository response XML")
|
||||
.arg(
|
||||
Arg::with_name("xml")
|
||||
.short("x")
|
||||
.long("xml")
|
||||
.value_name("FILE")
|
||||
.help("The name of the file containing the response.")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("list"))
|
||||
|
||||
.subcommand(SubCommand::with_name("sync")
|
||||
.about("Synchronise the directory specified by '-d'.")
|
||||
.arg(Arg::with_name("dir")
|
||||
.short("d")
|
||||
.long("dir")
|
||||
.value_name("FILE")
|
||||
.help("The directory that should be synced to the
|
||||
.subcommand(
|
||||
SubCommand::with_name("sync")
|
||||
.about("Synchronise the directory specified by '-d'.")
|
||||
.arg(
|
||||
Arg::with_name("dir")
|
||||
.short("d")
|
||||
.long("dir")
|
||||
.value_name("FILE")
|
||||
.help(
|
||||
"The directory that should be synced to the
|
||||
server. Note that entries here will be relative to
|
||||
the base rsync directory specified in the
|
||||
repository response.")
|
||||
.required(true))
|
||||
repository response.",
|
||||
)
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
|
||||
.get_matches();
|
||||
|
||||
let state_dir = {
|
||||
@@ -321,29 +331,30 @@ impl Options {
|
||||
let dir = PathBuf::from(dir);
|
||||
Command::Sync(dir)
|
||||
} else {
|
||||
return Err(OptionsError::NoCommand)
|
||||
return Err(OptionsError::NoCommand);
|
||||
}
|
||||
};
|
||||
|
||||
let format = Format::from(m.value_of("format").unwrap_or("none"))
|
||||
.map_err(|_| OptionsError::UnsupportedOutputFormat)?;
|
||||
|
||||
Ok(Options { state_dir, command, format })
|
||||
Ok(Options {
|
||||
state_dir,
|
||||
command,
|
||||
format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum OptionsError {
|
||||
|
||||
#[display(fmt="Specify a sub-command. See --help")]
|
||||
#[display(fmt = "Specify a sub-command. See --help")]
|
||||
NoCommand,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="Unsupported output format. Use text, json or none.")]
|
||||
#[display(fmt = "Unsupported output format. Use text, json or none.")]
|
||||
UnsupportedOutputFormat,
|
||||
}
|
||||
|
||||
@@ -353,31 +364,29 @@ impl From<io::Error> for OptionsError {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum Error {
|
||||
|
||||
#[display(fmt="This client is uninitialised.")]
|
||||
#[display(fmt = "This client is uninitialised.")]
|
||||
Uninitialised,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
SignerError(softsigner::SignerError),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
BuilderError(krill_commons::remote::builder::Error<softsigner::SignerError>),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
Rfc8183(rfc8183::Error),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
ClientError(ClientError),
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
FileError(file::Error),
|
||||
}
|
||||
|
||||
@@ -412,7 +421,9 @@ impl From<ClientError> for Error {
|
||||
}
|
||||
|
||||
impl From<file::Error> for Error {
|
||||
fn from(e: file::Error) -> Self { Error::FileError(e) }
|
||||
fn from(e: file::Error) -> Self {
|
||||
Error::FileError(e)
|
||||
}
|
||||
}
|
||||
|
||||
// For tests see main 'tests' folder
|
||||
|
||||
+24
-27
@@ -1,22 +1,24 @@
|
||||
extern crate clap;
|
||||
#[macro_use] extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
extern crate rpki;
|
||||
#[macro_use] extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
extern crate krill_commons;
|
||||
|
||||
pub mod apiclient;
|
||||
pub mod cmsclient;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use rpki::uri;
|
||||
use krill_commons::api::publication;
|
||||
use krill_commons::util::file;
|
||||
use rpki::uri;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn create_delta(
|
||||
list_reply: &publication::ListReply,
|
||||
dir: &PathBuf,
|
||||
base_rsync: &uri::Rsync
|
||||
base_rsync: &uri::Rsync,
|
||||
) -> Result<publication::PublishDelta, file::Error> {
|
||||
let mut delta_builder = publication::PublishDeltaBuilder::new();
|
||||
|
||||
@@ -25,9 +27,7 @@ pub fn create_delta(
|
||||
// loop through what the server has and find the ones to withdraw
|
||||
for p in list_reply.elements() {
|
||||
if current.iter().find(|c| c.uri() == p.uri()).is_none() {
|
||||
delta_builder.add_withdraw(
|
||||
publication::Withdraw::from_list_element(p)
|
||||
);
|
||||
delta_builder.add_withdraw(publication::Withdraw::from_list_element(p));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,11 @@ pub fn create_delta(
|
||||
// to be added to, which need to be updated at, or for which no change is
|
||||
// needed at the server.
|
||||
for f in current {
|
||||
match list_reply.elements().iter().find(|pbl| pbl.uri() == f.uri()) {
|
||||
match list_reply
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|pbl| pbl.uri() == f.uri())
|
||||
{
|
||||
None => delta_builder.add_publish(f.as_publish()),
|
||||
Some(pbl) => {
|
||||
if pbl.hash() != f.hash() {
|
||||
@@ -48,14 +52,13 @@ pub fn create_delta(
|
||||
Ok(delta_builder.finish())
|
||||
}
|
||||
|
||||
|
||||
//------------ Format --------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Format {
|
||||
Json,
|
||||
Text,
|
||||
None
|
||||
None,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
@@ -64,14 +67,13 @@ impl Format {
|
||||
"text" => Ok(Format::Text),
|
||||
"none" => Ok(Format::None),
|
||||
"json" => Ok(Format::Json),
|
||||
_ => Err(UnsupportedFormat)
|
||||
_ => Err(UnsupportedFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnsupportedFormat;
|
||||
|
||||
|
||||
//------------ ApiResponse ---------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -83,28 +85,23 @@ pub enum ApiResponse {
|
||||
impl ApiResponse {
|
||||
pub fn report(&self, format: &Format) {
|
||||
match format {
|
||||
Format::None => {}, // done,
|
||||
Format::None => {} // done,
|
||||
Format::Json => {
|
||||
match self {
|
||||
ApiResponse::Success => {}, // nothing to report
|
||||
ApiResponse::Success => {} // nothing to report
|
||||
ApiResponse::List(reply) => {
|
||||
println!("{}", serde_json::to_string(reply).unwrap());
|
||||
}
|
||||
}
|
||||
},
|
||||
Format::Text => {
|
||||
match self {
|
||||
ApiResponse::Success => println!("success"),
|
||||
ApiResponse::List(list) => {
|
||||
for el in list.elements() {
|
||||
println!("{} {}",
|
||||
el.hash().to_string(),
|
||||
el.uri().to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
Format::Text => match self {
|
||||
ApiResponse::Success => println!("success"),
|
||||
ApiResponse::List(list) => {
|
||||
for el in list.elements() {
|
||||
println!("{} {}", el.hash().to_string(), el.uri().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,14 +1,15 @@
|
||||
extern crate bytes;
|
||||
#[macro_use] extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
extern crate rand;
|
||||
extern crate rpki;
|
||||
#[macro_use] extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
extern crate krill_commons;
|
||||
|
||||
pub mod publishers;
|
||||
pub mod repo;
|
||||
|
||||
mod pubserver;
|
||||
pub use pubserver::PubServer;
|
||||
pub use pubserver::Error;
|
||||
|
||||
pub use pubserver::PubServer;
|
||||
|
||||
+36
-81
@@ -1,23 +1,8 @@
|
||||
use rpki::uri;
|
||||
use krill_commons::api::admin::{Handle, PublisherDetails, PublisherRequest, Token};
|
||||
use krill_commons::api::publication;
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
PublisherDetails,
|
||||
PublisherRequest,
|
||||
Token
|
||||
};
|
||||
use krill_commons::api::rrdp::{
|
||||
CurrentObjects,
|
||||
DeltaElements,
|
||||
VerificationError
|
||||
};
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate,
|
||||
CommandDetails,
|
||||
StoredEvent,
|
||||
SentCommand
|
||||
};
|
||||
|
||||
use krill_commons::api::rrdp::{CurrentObjects, DeltaElements, VerificationError};
|
||||
use krill_commons::eventsourcing::{Aggregate, CommandDetails, SentCommand, StoredEvent};
|
||||
use rpki::uri;
|
||||
|
||||
//------------ PublisherInit -------------------------------------------------
|
||||
|
||||
@@ -37,7 +22,6 @@ impl InitPublisherDetails {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherEvent ------------------------------------------------
|
||||
|
||||
pub type PublisherEvent = StoredEvent<PublisherEventDetails>;
|
||||
@@ -45,35 +29,19 @@ pub type PublisherEvent = StoredEvent<PublisherEventDetails>;
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum PublisherEventDetails {
|
||||
Deactivated,
|
||||
Published(DeltaElements)
|
||||
Published(DeltaElements),
|
||||
}
|
||||
|
||||
impl PublisherEventDetails {
|
||||
pub fn deactivated(
|
||||
handle: &Handle,
|
||||
version: u64
|
||||
) -> PublisherEvent {
|
||||
PublisherEvent::new(
|
||||
&handle,
|
||||
version,
|
||||
PublisherEventDetails::Deactivated
|
||||
)
|
||||
pub fn deactivated(handle: &Handle, version: u64) -> PublisherEvent {
|
||||
PublisherEvent::new(&handle, version, PublisherEventDetails::Deactivated)
|
||||
}
|
||||
|
||||
pub fn published(
|
||||
handle: &Handle,
|
||||
version: u64,
|
||||
delta: DeltaElements
|
||||
) -> PublisherEvent {
|
||||
PublisherEvent::new(
|
||||
&handle,
|
||||
version,
|
||||
PublisherEventDetails::Published(delta)
|
||||
)
|
||||
pub fn published(handle: &Handle, version: u64, delta: DeltaElements) -> PublisherEvent {
|
||||
PublisherEvent::new(&handle, version, PublisherEventDetails::Published(delta))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherCommand ----------------------------------------------
|
||||
|
||||
pub type PublisherCommand = SentCommand<PublisherCommandDetails>;
|
||||
@@ -81,7 +49,7 @@ pub type PublisherCommand = SentCommand<PublisherCommandDetails>;
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub enum PublisherCommandDetails {
|
||||
Deactivate,
|
||||
Publish(publication::PublishDelta)
|
||||
Publish(publication::PublishDelta),
|
||||
}
|
||||
|
||||
impl CommandDetails for PublisherCommandDetails {
|
||||
@@ -90,26 +58,14 @@ impl CommandDetails for PublisherCommandDetails {
|
||||
|
||||
impl PublisherCommandDetails {
|
||||
pub fn deactivate(handle: &Handle) -> PublisherCommand {
|
||||
PublisherCommand::new(
|
||||
&handle,
|
||||
None,
|
||||
PublisherCommandDetails::Deactivate
|
||||
)
|
||||
PublisherCommand::new(&handle, None, PublisherCommandDetails::Deactivate)
|
||||
}
|
||||
|
||||
pub fn publish(
|
||||
handle: &Handle,
|
||||
delta: publication::PublishDelta
|
||||
) -> PublisherCommand {
|
||||
PublisherCommand::new(
|
||||
&handle,
|
||||
None,
|
||||
PublisherCommandDetails::Publish(delta)
|
||||
)
|
||||
pub fn publish(handle: &Handle, delta: publication::PublishDelta) -> PublisherCommand {
|
||||
PublisherCommand::new(&handle, None, PublisherCommandDetails::Publish(delta))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherError ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Display)]
|
||||
@@ -117,7 +73,7 @@ pub enum PublisherError {
|
||||
#[display(fmt = "Publisher is (already) de-activated")]
|
||||
Deactivated,
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
#[display(fmt = "{}", _0)]
|
||||
VerificationError(VerificationError),
|
||||
}
|
||||
|
||||
@@ -129,25 +85,24 @@ impl From<VerificationError> for PublisherError {
|
||||
|
||||
impl std::error::Error for PublisherError {}
|
||||
|
||||
|
||||
//------------ Publisher -----------------------------------------------------
|
||||
|
||||
/// This type defines Publisher CAs that are allowed to publish.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Publisher {
|
||||
/// Aggregate house keeping
|
||||
handle: Handle,
|
||||
version: u64,
|
||||
handle: Handle,
|
||||
version: u64,
|
||||
deactivated: bool,
|
||||
|
||||
/// Publication jail for this publisher
|
||||
base_uri: uri::Rsync,
|
||||
base_uri: uri::Rsync,
|
||||
|
||||
/// The token used by the API
|
||||
token: Token,
|
||||
token: Token,
|
||||
|
||||
/// All objects currently published by this publisher, by hash
|
||||
current_objects: CurrentObjects
|
||||
current_objects: CurrentObjects,
|
||||
}
|
||||
|
||||
/// # Accessors
|
||||
@@ -156,7 +111,9 @@ impl Publisher {
|
||||
&self.handle
|
||||
}
|
||||
|
||||
pub fn is_deactivated(&self) -> bool { self.deactivated }
|
||||
pub fn is_deactivated(&self) -> bool {
|
||||
self.deactivated
|
||||
}
|
||||
|
||||
pub fn token(&self) -> &Token {
|
||||
&self.token
|
||||
@@ -167,25 +124,22 @@ impl Publisher {
|
||||
}
|
||||
|
||||
pub fn as_api_details(&self) -> PublisherDetails {
|
||||
PublisherDetails::new(
|
||||
self.handle.as_str(), self.deactivated, &self.base_uri
|
||||
)
|
||||
PublisherDetails::new(self.handle.as_str(), self.deactivated, &self.base_uri)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Life cycle
|
||||
///
|
||||
impl Publisher {
|
||||
|
||||
fn create(event: PublisherInit) -> Self {
|
||||
let (handle, _version, init) = event.unwrap();
|
||||
Publisher {
|
||||
handle,
|
||||
version: 1,
|
||||
deactivated: false,
|
||||
token: init.token,
|
||||
base_uri: init.base_uri,
|
||||
current_objects: CurrentObjects::default()
|
||||
version: 1,
|
||||
deactivated: false,
|
||||
token: init.token,
|
||||
base_uri: init.base_uri,
|
||||
current_objects: CurrentObjects::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +153,6 @@ impl Publisher {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Publication protocol
|
||||
///
|
||||
impl Publisher {
|
||||
@@ -216,13 +169,16 @@ impl Publisher {
|
||||
/// provided that it's legitimate.
|
||||
fn process_delta_cmd(
|
||||
&self,
|
||||
delta: publication::PublishDelta
|
||||
delta: publication::PublishDelta,
|
||||
) -> Result<Vec<PublisherEvent>, PublisherError> {
|
||||
|
||||
let delta = DeltaElements::from(delta);
|
||||
self.current_objects.verify_delta(&delta, &self.base_uri)?;
|
||||
|
||||
Ok(vec![PublisherEventDetails::published(&self.handle, self.version, delta)])
|
||||
Ok(vec![PublisherEventDetails::published(
|
||||
&self.handle,
|
||||
self.version,
|
||||
delta,
|
||||
)])
|
||||
}
|
||||
|
||||
fn apply_delta(&mut self, delta: DeltaElements) {
|
||||
@@ -230,7 +186,6 @@ impl Publisher {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Aggregate for Publisher {
|
||||
type Command = PublisherCommand;
|
||||
type Event = PublisherEvent;
|
||||
@@ -248,7 +203,7 @@ impl Aggregate for Publisher {
|
||||
fn apply(&mut self, event: Self::Event) {
|
||||
match event.into_details() {
|
||||
PublisherEventDetails::Deactivated => self.deactivated = true,
|
||||
PublisherEventDetails::Published(delta) => self.apply_delta(delta)
|
||||
PublisherEventDetails::Published(delta) => self.apply_delta(delta),
|
||||
}
|
||||
self.version += 1;
|
||||
}
|
||||
@@ -256,7 +211,7 @@ impl Aggregate for Publisher {
|
||||
fn process_command(&self, command: Self::Command) -> Result<Vec<Self::Event>, Self::Error> {
|
||||
match command.into_details() {
|
||||
PublisherCommandDetails::Deactivate => self.deactivate(),
|
||||
PublisherCommandDetails::Publish(delta) => self.process_delta_cmd(delta)
|
||||
PublisherCommandDetails::Publish(delta) => self.process_delta_cmd(delta),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+128
-191
@@ -1,39 +1,22 @@
|
||||
use crate::publishers::{
|
||||
InitPublisherDetails, Publisher, PublisherCommand, PublisherCommandDetails, PublisherError,
|
||||
PublisherEventDetails,
|
||||
};
|
||||
use crate::repo::{
|
||||
self, RetentionTime, RrdpCommandDetails, RrdpInitDetails, RrdpServer, RrdpServerError,
|
||||
RsyncdStore,
|
||||
};
|
||||
use krill_commons::api::admin::{Handle, PublisherRequest};
|
||||
use krill_commons::api::ca::RepoInfo;
|
||||
use krill_commons::api::publication;
|
||||
use krill_commons::api::rrdp::DeltaElements;
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate, AggregateStore, AggregateStoreError, Command, DiskAggregateStore,
|
||||
};
|
||||
use rpki::uri;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use rpki::uri;
|
||||
use krill_commons::api::publication;
|
||||
use krill_commons::api::admin::{
|
||||
Handle,
|
||||
PublisherRequest
|
||||
};
|
||||
use krill_commons::api::ca::RepoInfo;
|
||||
use krill_commons::api::rrdp::DeltaElements;
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate,
|
||||
AggregateStore,
|
||||
AggregateStoreError,
|
||||
Command,
|
||||
DiskAggregateStore,
|
||||
};
|
||||
use crate::publishers::{
|
||||
Publisher,
|
||||
PublisherCommand,
|
||||
PublisherCommandDetails,
|
||||
PublisherError,
|
||||
PublisherEventDetails,
|
||||
InitPublisherDetails
|
||||
};
|
||||
use crate::repo::{
|
||||
self,
|
||||
RetentionTime,
|
||||
RrdpCommandDetails,
|
||||
RrdpInitDetails,
|
||||
RrdpServer,
|
||||
RrdpServerError,
|
||||
RsyncdStore,
|
||||
};
|
||||
|
||||
|
||||
//------------ PubServer -----------------------------------------------------
|
||||
|
||||
@@ -45,26 +28,32 @@ pub struct PubServer {
|
||||
rsyncd_store: RsyncdStore,
|
||||
store: Arc<DiskAggregateStore<Publisher>>,
|
||||
base_rsync_uri: uri::Rsync, // jail for the publishers,
|
||||
command_lock: Mutex<()> // Only one command at the time.
|
||||
command_lock: Mutex<()>, // Only one command at the time.
|
||||
}
|
||||
|
||||
impl PubServer {
|
||||
pub fn build(
|
||||
base_rsync_uri: uri::Rsync,
|
||||
base_http_uri: uri::Https, // for the RRDP files
|
||||
repo_dir: PathBuf, // for the RRDP and rsync files
|
||||
work_dir: &PathBuf // for the aggregate stores
|
||||
repo_dir: PathBuf, // for the RRDP and rsync files
|
||||
work_dir: &PathBuf, // for the aggregate stores
|
||||
) -> Result<Self, Error> {
|
||||
let rrdp_store = Arc::new(DiskAggregateStore::<RrdpServer>::new(work_dir, "repo-server")?);
|
||||
let rrdp_store = Arc::new(DiskAggregateStore::<RrdpServer>::new(
|
||||
work_dir,
|
||||
"repo-server",
|
||||
)?);
|
||||
|
||||
let rsyncd_store = RsyncdStore::build(&repo_dir)?;
|
||||
|
||||
if ! rrdp_store.has(&repo::id()) {
|
||||
if !rrdp_store.has(&repo::id()) {
|
||||
let init = RrdpInitDetails::init_new(base_http_uri, repo_dir);
|
||||
rrdp_store.add(init)?;
|
||||
}
|
||||
|
||||
let store = Arc::new(DiskAggregateStore::<Publisher>::new(work_dir, "publishers")?);
|
||||
let store = Arc::new(DiskAggregateStore::<Publisher>::new(
|
||||
work_dir,
|
||||
"publishers",
|
||||
)?);
|
||||
|
||||
let command_lock = Mutex::new(());
|
||||
|
||||
@@ -73,7 +62,7 @@ impl PubServer {
|
||||
rsyncd_store,
|
||||
store,
|
||||
base_rsync_uri,
|
||||
command_lock
|
||||
command_lock,
|
||||
};
|
||||
|
||||
Ok(pubserver)
|
||||
@@ -92,21 +81,16 @@ impl PubServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Publication Protocol support
|
||||
///
|
||||
impl PubServer {
|
||||
|
||||
fn rrdp_server(&self) -> Result<Arc<RrdpServer>, Error> {
|
||||
self.rrdp_store.get_latest(&repo::id()).map_err(Error::AggregateStoreError)
|
||||
self.rrdp_store
|
||||
.get_latest(&repo::id())
|
||||
.map_err(Error::AggregateStoreError)
|
||||
}
|
||||
|
||||
pub fn publish(
|
||||
&self,
|
||||
handle: &Handle,
|
||||
delta: publication::PublishDelta
|
||||
) -> Result<(), Error> {
|
||||
|
||||
pub fn publish(&self, handle: &Handle, delta: publication::PublishDelta) -> Result<(), Error> {
|
||||
// Only do one update at a time.
|
||||
let _lock = self.command_lock.lock().unwrap();
|
||||
|
||||
@@ -125,12 +109,16 @@ impl PubServer {
|
||||
let rrdp = self.rrdp_server()?;
|
||||
let add_cmd = RrdpCommandDetails::add_delta(delta);
|
||||
let rrdp_add_delta_events = rrdp.process_command(add_cmd)?;
|
||||
let rrdp = self.rrdp_store.update(&repo_id, rrdp, rrdp_add_delta_events)?;
|
||||
let rrdp = self
|
||||
.rrdp_store
|
||||
.update(&repo_id, rrdp, rrdp_add_delta_events)?;
|
||||
|
||||
// Trigger publication of the RRDP files
|
||||
let publish_cmd = RrdpCommandDetails::publish();
|
||||
let rrdp_publish_events = rrdp.process_command(publish_cmd)?;
|
||||
let rrdp = self.rrdp_store.update(&repo_id, rrdp, rrdp_publish_events)?;
|
||||
let rrdp = self
|
||||
.rrdp_store
|
||||
.update(&repo_id, rrdp, rrdp_publish_events)?;
|
||||
|
||||
// Clean up old files
|
||||
let retention = RetentionTime::from_secs(0);
|
||||
@@ -142,31 +130,26 @@ impl PubServer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list(
|
||||
&self,
|
||||
handle: &Handle
|
||||
) -> Result<publication::ListReply, Error> {
|
||||
pub fn list(&self, handle: &Handle) -> Result<publication::ListReply, Error> {
|
||||
match self.get_publisher(handle)? {
|
||||
Some(publisher) => Ok(publisher.list_current()),
|
||||
None => Err(Error::UnknownPublisher(handle.to_string()))
|
||||
None => Err(Error::UnknownPublisher(handle.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// # Publishing
|
||||
///
|
||||
impl PubServer {
|
||||
|
||||
fn verify_handle(&self, handle: &Handle) -> Result<(), Error> {
|
||||
let name = handle.as_str();
|
||||
|
||||
if ! name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
|
||||
return Err(Error::InvalidHandle(name.to_string()))
|
||||
if !name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
|
||||
return Err(Error::InvalidHandle(name.to_string()));
|
||||
}
|
||||
|
||||
if self.store.has(handle) {
|
||||
return Err(Error::DuplicatePublisher(name.to_string()))
|
||||
return Err(Error::DuplicatePublisher(name.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -186,12 +169,10 @@ impl PubServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_publisher(
|
||||
&self,
|
||||
handle: &Handle
|
||||
) -> Result<Option<Arc<Publisher>>, Error> {
|
||||
pub fn get_publisher(&self, handle: &Handle) -> Result<Option<Arc<Publisher>>, Error> {
|
||||
if self.store.has(handle) {
|
||||
self.store.get_latest(handle)
|
||||
self.store
|
||||
.get_latest(handle)
|
||||
.map(Some)
|
||||
.map_err(Error::AggregateStoreError)
|
||||
} else {
|
||||
@@ -201,10 +182,7 @@ impl PubServer {
|
||||
|
||||
/// Adds a publisher. Will complain if a publisher already exists for this
|
||||
/// handle. Will also verify that the base_uri is allowed.
|
||||
pub fn create_publisher(
|
||||
&self,
|
||||
req: PublisherRequest
|
||||
) -> Result<(), Error> {
|
||||
pub fn create_publisher(&self, req: PublisherRequest) -> Result<(), Error> {
|
||||
self.verify_handle(req.handle())?;
|
||||
self.verify_base_uri(req.base_uri())?;
|
||||
|
||||
@@ -224,24 +202,16 @@ impl PubServer {
|
||||
/// re-activation in future. Reason is that we never forget the history
|
||||
/// of the old publisher, and if handles are re-used by different
|
||||
/// entities that would get confusing.
|
||||
pub fn deactivate_publisher(
|
||||
&self,
|
||||
handle: &Handle
|
||||
) -> Result<(), Error> {
|
||||
pub fn deactivate_publisher(&self, handle: &Handle) -> Result<(), Error> {
|
||||
let cmd = PublisherCommandDetails::deactivate(handle);
|
||||
self.command_publisher(cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Apply a command to a publisher. If this was a successful publication
|
||||
/// command, then return the delta so that it can be published by the
|
||||
/// RRDP server.
|
||||
fn command_publisher(
|
||||
&self,
|
||||
command: PublisherCommand
|
||||
) -> Result<Option<DeltaElements>, Error> {
|
||||
|
||||
fn command_publisher(&self, command: PublisherCommand) -> Result<Option<DeltaElements>, Error> {
|
||||
let handle = command.handle().clone();
|
||||
|
||||
match self.get_publisher(&handle)? {
|
||||
@@ -251,9 +221,7 @@ impl PubServer {
|
||||
|
||||
if let Some(version) = command.version() {
|
||||
if version != pbl.version() {
|
||||
return Err(
|
||||
Error::ConcurrentModification(version, pbl.version())
|
||||
)
|
||||
return Err(Error::ConcurrentModification(version, pbl.version()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +239,6 @@ impl PubServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
@@ -281,10 +248,16 @@ pub enum Error {
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt = "The publisher handle may only contain a-ZA-Z0-9 and _. You sent: {}", _0)]
|
||||
#[display(
|
||||
fmt = "The publisher handle may only contain a-ZA-Z0-9 and _. You sent: {}",
|
||||
_0
|
||||
)]
|
||||
InvalidHandle(String),
|
||||
|
||||
#[display(fmt = "Duplicate publisher with name: {} (note: might be de-activated).", _0)]
|
||||
#[display(
|
||||
fmt = "Duplicate publisher with name: {} (note: might be de-activated).",
|
||||
_0
|
||||
)]
|
||||
DuplicatePublisher(String),
|
||||
|
||||
#[display(fmt = "Unknown publisher with name: {}.", _0)]
|
||||
@@ -307,37 +280,44 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self { Error::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublisherError> for Error {
|
||||
fn from(e: PublisherError) -> Self { Error::PublisherError(e) }
|
||||
fn from(e: PublisherError) -> Self {
|
||||
Error::PublisherError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RrdpServerError> for Error {
|
||||
fn from(e: RrdpServerError) -> Self { Error::RrdpServerError(e) }
|
||||
fn from(e: RrdpServerError) -> Self {
|
||||
Error::RrdpServerError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AggregateStoreError> for Error {
|
||||
fn from(e: AggregateStoreError) -> Self { Error::AggregateStoreError(e) }
|
||||
fn from(e: AggregateStoreError) -> Self {
|
||||
Error::AggregateStoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
|
||||
//------------ Tests ---------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::path::PathBuf;
|
||||
use bytes::Bytes;
|
||||
use krill_commons::api::admin::Token;
|
||||
use krill_commons::api::publication::PublishDeltaBuilder;
|
||||
use krill_commons::api::rrdp::VerificationError;
|
||||
use krill_commons::util::file::CurrentFile;
|
||||
use krill_commons::util::test;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn server_base_uri() -> uri::Rsync {
|
||||
test::rsync("rsync://localhost/repo/")
|
||||
@@ -347,10 +327,7 @@ mod tests {
|
||||
test::https("https://localhost/rrdp/")
|
||||
}
|
||||
|
||||
fn make_publisher_req(
|
||||
handle: &str,
|
||||
uri: &str,
|
||||
) -> PublisherRequest {
|
||||
fn make_publisher_req(handle: &str, uri: &str) -> PublisherRequest {
|
||||
let base_uri = test::rsync(uri);
|
||||
let handle = Handle::from(handle);
|
||||
let token = Token::from("secret");
|
||||
@@ -366,17 +343,15 @@ mod tests {
|
||||
server_base_uri(),
|
||||
server_base_http_uri(),
|
||||
base_dir,
|
||||
work_dir
|
||||
).unwrap()
|
||||
work_dir,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_add_publisher() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/repo/alice/");
|
||||
|
||||
let server = make_server(&d);
|
||||
server.create_publisher(publisher_req).unwrap();
|
||||
@@ -391,17 +366,12 @@ mod tests {
|
||||
#[test]
|
||||
fn should_refuse_invalid_publisher_handle() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice&",
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice&", "rsync://localhost/repo/alice/");
|
||||
|
||||
let server = make_server(&d);
|
||||
match server.create_publisher(publisher_req) {
|
||||
Err(Error::InvalidHandle(handle)) => {
|
||||
assert_eq!(handle, "alice&".to_string())
|
||||
},
|
||||
_ => panic!("Expected error")
|
||||
Err(Error::InvalidHandle(handle)) => assert_eq!(handle, "alice&".to_string()),
|
||||
_ => panic!("Expected error"),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -409,15 +379,12 @@ mod tests {
|
||||
#[test]
|
||||
fn should_refuse_base_uri_not_ending_with_slash() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/repo/alice",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/repo/alice");
|
||||
|
||||
let server = make_server(&d);
|
||||
match server.create_publisher(publisher_req) {
|
||||
Err(Error::InvalidBaseUri) => { },
|
||||
_ => panic!("Expected error")
|
||||
Err(Error::InvalidBaseUri) => {}
|
||||
_ => panic!("Expected error"),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -425,15 +392,12 @@ mod tests {
|
||||
#[test]
|
||||
fn should_refuse_base_uri_outside_of_server_base() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/outside/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/outside/alice/");
|
||||
|
||||
let server = make_server(&d);
|
||||
match server.create_publisher(publisher_req) {
|
||||
Err(Error::InvalidBaseUri) => { },
|
||||
_ => panic!("Expected error")
|
||||
Err(Error::InvalidBaseUri) => {}
|
||||
_ => panic!("Expected error"),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -441,18 +405,13 @@ mod tests {
|
||||
#[test]
|
||||
fn should_not_add_publisher_twice() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/repo/alice/");
|
||||
|
||||
let server = make_server(&d);
|
||||
server.create_publisher(publisher_req.clone()).unwrap();
|
||||
match server.create_publisher(publisher_req) {
|
||||
Err(Error::DuplicatePublisher(name)) => {
|
||||
assert_eq!(name, "alice".to_string())
|
||||
},
|
||||
_ => panic!("Expected error")
|
||||
Err(Error::DuplicatePublisher(name)) => assert_eq!(name, "alice".to_string()),
|
||||
_ => panic!("Expected error"),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -464,10 +423,8 @@ mod tests {
|
||||
let handle = Handle::from("alice");
|
||||
|
||||
// create publisher
|
||||
let publisher_req = make_publisher_req(
|
||||
handle.as_str(),
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req =
|
||||
make_publisher_req(handle.as_str(), "rsync://localhost/repo/alice/");
|
||||
server.create_publisher(publisher_req).unwrap();
|
||||
|
||||
// expect to see it in the list
|
||||
@@ -481,17 +438,13 @@ mod tests {
|
||||
// expect that it is now inactive
|
||||
let alice = server.get_publisher(&handle).unwrap().unwrap();
|
||||
assert!(alice.is_deactivated())
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_list_files() {
|
||||
test::test_under_tmp(|d| {
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/repo/alice/");
|
||||
let handle = Handle::from("alice");
|
||||
|
||||
let server = make_server(&d);
|
||||
@@ -510,15 +463,12 @@ mod tests {
|
||||
// get the file out of a list_reply
|
||||
fn find_in_reply<'a>(
|
||||
reply: &'a publication::ListReply,
|
||||
uri: &uri::Rsync
|
||||
uri: &uri::Rsync,
|
||||
) -> Option<&'a publication::ListElement> {
|
||||
reply.elements().iter().find(|e| e.uri() == uri)
|
||||
}
|
||||
|
||||
let publisher_req = make_publisher_req(
|
||||
"alice",
|
||||
"rsync://localhost/repo/alice/",
|
||||
);
|
||||
let publisher_req = make_publisher_req("alice", "rsync://localhost/repo/alice/");
|
||||
let handle = Handle::from("alice");
|
||||
|
||||
let server = make_server(&d);
|
||||
@@ -527,12 +477,12 @@ mod tests {
|
||||
// Publish a single file
|
||||
let file1 = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/file.txt"),
|
||||
&Bytes::from("example content")
|
||||
&Bytes::from("example content"),
|
||||
);
|
||||
|
||||
let file2 = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/file2.txt"),
|
||||
&Bytes::from("example content 2")
|
||||
&Bytes::from("example content 2"),
|
||||
);
|
||||
|
||||
let mut builder = PublishDeltaBuilder::new();
|
||||
@@ -549,11 +499,13 @@ mod tests {
|
||||
assert!(find_in_reply(
|
||||
&list_reply,
|
||||
&test::rsync("rsync://localhost/repo/alice/file.txt")
|
||||
).is_some());
|
||||
)
|
||||
.is_some());
|
||||
assert!(find_in_reply(
|
||||
&list_reply,
|
||||
&test::rsync("rsync://localhost/repo/alice/file2.txt")
|
||||
).is_some());
|
||||
)
|
||||
.is_some());
|
||||
|
||||
// Update
|
||||
// - update file
|
||||
@@ -562,12 +514,12 @@ mod tests {
|
||||
|
||||
let file1_update = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/file.txt"),
|
||||
&Bytes::from("example content - updated")
|
||||
&Bytes::from("example content - updated"),
|
||||
);
|
||||
|
||||
let file3 = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/file3.txt"),
|
||||
&Bytes::from("example content 3")
|
||||
&Bytes::from("example content 3"),
|
||||
);
|
||||
|
||||
let mut builder = PublishDeltaBuilder::new();
|
||||
@@ -586,58 +538,54 @@ mod tests {
|
||||
assert!(find_in_reply(
|
||||
&list_reply,
|
||||
&test::rsync("rsync://localhost/repo/alice/file.txt")
|
||||
).is_some());
|
||||
)
|
||||
.is_some());
|
||||
assert_eq!(
|
||||
find_in_reply(
|
||||
&list_reply,
|
||||
&test::rsync("rsync://localhost/repo/alice/file.txt")
|
||||
).unwrap().hash(),
|
||||
)
|
||||
.unwrap()
|
||||
.hash(),
|
||||
file1_update.hash()
|
||||
);
|
||||
assert!(find_in_reply(
|
||||
&list_reply,
|
||||
&test::rsync("rsync://localhost/repo/alice/file3.txt")
|
||||
).is_some());
|
||||
)
|
||||
.is_some());
|
||||
|
||||
// Should reject publish outside of base uri
|
||||
let file_outside = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/bob/file.txt"),
|
||||
&Bytes::from("irrelevant")
|
||||
&Bytes::from("irrelevant"),
|
||||
);
|
||||
let mut builder = PublishDeltaBuilder::new();
|
||||
builder.add_publish(file_outside.as_publish());
|
||||
let delta = builder.finish();
|
||||
|
||||
match server.publish(&handle, delta) {
|
||||
Err(
|
||||
Error::PublisherError(
|
||||
PublisherError::VerificationError(
|
||||
VerificationError::UriOutsideJail(_, _)
|
||||
)
|
||||
)
|
||||
) => {}, // ok
|
||||
_ => panic!("Expected error publishing outside of base uri jail")
|
||||
Err(Error::PublisherError(PublisherError::VerificationError(
|
||||
VerificationError::UriOutsideJail(_, _),
|
||||
))) => {} // ok
|
||||
_ => panic!("Expected error publishing outside of base uri jail"),
|
||||
}
|
||||
|
||||
// Should reject update of file that does not exist
|
||||
let file2_update = CurrentFile::new(
|
||||
test::rsync("rsync://localhost/repo/alice/file2.txt"),
|
||||
&Bytes::from("example content 2 updated")
|
||||
&Bytes::from("example content 2 updated"),
|
||||
); // file2 was removed
|
||||
let mut builder = PublishDeltaBuilder::new();
|
||||
builder.add_update(file2_update.as_update(file2.hash()));
|
||||
let delta = builder.finish();
|
||||
|
||||
match server.publish(&handle, delta) {
|
||||
Err(
|
||||
Error::PublisherError(
|
||||
PublisherError::VerificationError(
|
||||
VerificationError::NoObjectForHashAndOrUri(_)
|
||||
)
|
||||
)
|
||||
) => {},
|
||||
Err(Error::PublisherError(PublisherError::VerificationError(
|
||||
VerificationError::NoObjectForHashAndOrUri(_),
|
||||
))) => {}
|
||||
// ok
|
||||
_ => panic!("Expected error when file for update can't be found")
|
||||
_ => panic!("Expected error when file for update can't be found"),
|
||||
}
|
||||
|
||||
// should reject withdraw for file that does not exist
|
||||
@@ -647,14 +595,10 @@ mod tests {
|
||||
let cmd = PublisherCommandDetails::publish(&handle, delta);
|
||||
|
||||
match server.command_publisher(cmd) {
|
||||
Err(
|
||||
Error::PublisherError(
|
||||
PublisherError::VerificationError(
|
||||
VerificationError::NoObjectForHashAndOrUri(_)
|
||||
)
|
||||
)
|
||||
) => {}, // ok
|
||||
_ => panic!("Expected error withdrawing file that does not exist")
|
||||
Err(Error::PublisherError(PublisherError::VerificationError(
|
||||
VerificationError::NoObjectForHashAndOrUri(_),
|
||||
))) => {} // ok
|
||||
_ => panic!("Expected error withdrawing file that does not exist"),
|
||||
}
|
||||
|
||||
// should reject publish for file that does exist
|
||||
@@ -663,18 +607,11 @@ mod tests {
|
||||
let delta = builder.finish();
|
||||
|
||||
match server.publish(&handle, delta) {
|
||||
Err(
|
||||
Error::PublisherError(
|
||||
PublisherError::VerificationError
|
||||
(VerificationError::ObjectAlreadyPresent(uri)
|
||||
)
|
||||
)
|
||||
) => { assert_eq!(
|
||||
uri,
|
||||
test::rsync("rsync://localhost/repo/alice/file3.txt")
|
||||
)},
|
||||
_ => panic!("Expected error publishing file that already exists")
|
||||
Err(Error::PublisherError(PublisherError::VerificationError(
|
||||
VerificationError::ObjectAlreadyPresent(uri),
|
||||
))) => assert_eq!(uri, test::rsync("rsync://localhost/repo/alice/file3.txt")),
|
||||
_ => panic!("Expected error publishing file that already exists"),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
-122
@@ -1,29 +1,14 @@
|
||||
use std::{io, fs};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use rpki::uri;
|
||||
use krill_commons::api::admin::Handle;
|
||||
use krill_commons::api::rrdp::{
|
||||
Delta,
|
||||
DeltaElements,
|
||||
DeltaRef,
|
||||
FileRef,
|
||||
Notification,
|
||||
NotificationUpdate,
|
||||
Snapshot,
|
||||
Delta, DeltaElements, DeltaRef, FileRef, Notification, NotificationUpdate, Snapshot,
|
||||
SnapshotRef,
|
||||
};
|
||||
use krill_commons::eventsourcing::{
|
||||
Aggregate,
|
||||
CommandDetails,
|
||||
StoredEvent,
|
||||
SentCommand,
|
||||
};
|
||||
use krill_commons::util::{
|
||||
file,
|
||||
Time
|
||||
};
|
||||
|
||||
use krill_commons::eventsourcing::{Aggregate, CommandDetails, SentCommand, StoredEvent};
|
||||
use krill_commons::util::{file, Time};
|
||||
use rpki::uri;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use std::{fs, io};
|
||||
|
||||
const RRDP_FOLDER: &str = "rrdp";
|
||||
const RSYNC_FOLDER: &str = "rsync";
|
||||
@@ -33,7 +18,6 @@ pub fn id() -> Handle {
|
||||
Handle::from(ID)
|
||||
}
|
||||
|
||||
|
||||
//------------ RrdpInit ------------------------------------------------------
|
||||
|
||||
pub type RrdpInit = StoredEvent<RrdpInitDetails>;
|
||||
@@ -42,7 +26,7 @@ pub type RrdpInit = StoredEvent<RrdpInitDetails>;
|
||||
pub struct RrdpInitDetails {
|
||||
session: String,
|
||||
base_uri: uri::Https,
|
||||
repo_dir: PathBuf
|
||||
repo_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl RrdpInitDetails {
|
||||
@@ -55,12 +39,15 @@ impl RrdpInitDetails {
|
||||
StoredEvent::new(
|
||||
&id(),
|
||||
0,
|
||||
RrdpInitDetails { session, base_uri, repo_dir }
|
||||
RrdpInitDetails {
|
||||
session,
|
||||
base_uri,
|
||||
repo_dir,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RrdpEvent ------------------------------------------------------
|
||||
|
||||
pub type RrdpEvent = StoredEvent<RrdpEventDetails>;
|
||||
@@ -70,7 +57,7 @@ pub type RrdpEvent = StoredEvent<RrdpEventDetails>;
|
||||
pub enum RrdpEventDetails {
|
||||
AddedDelta(Delta),
|
||||
UpdatedNotification(NotificationUpdate),
|
||||
CleanedUp(Time)
|
||||
CleanedUp(Time),
|
||||
}
|
||||
|
||||
impl RrdpEventDetails {
|
||||
@@ -78,24 +65,15 @@ impl RrdpEventDetails {
|
||||
StoredEvent::new(id, ver, RrdpEventDetails::AddedDelta(delta))
|
||||
}
|
||||
|
||||
fn updated_notification(
|
||||
id: &Handle,
|
||||
ver: u64,
|
||||
notif: NotificationUpdate
|
||||
) -> RrdpEvent {
|
||||
fn updated_notification(id: &Handle, ver: u64, notif: NotificationUpdate) -> RrdpEvent {
|
||||
StoredEvent::new(id, ver, RrdpEventDetails::UpdatedNotification(notif))
|
||||
}
|
||||
|
||||
fn cleaned_up(
|
||||
id: &Handle,
|
||||
ver: u64,
|
||||
time: Time
|
||||
) -> RrdpEvent {
|
||||
fn cleaned_up(id: &Handle, ver: u64, time: Time) -> RrdpEvent {
|
||||
StoredEvent::new(id, ver, RrdpEventDetails::CleanedUp(time))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RrdpCommand ---------------------------------------------------
|
||||
|
||||
pub type RrdpCommand = SentCommand<RrdpCommandDetails>;
|
||||
@@ -104,7 +82,7 @@ pub type RrdpCommand = SentCommand<RrdpCommandDetails>;
|
||||
pub enum RrdpCommandDetails {
|
||||
AddDelta(DeltaElements),
|
||||
Publish,
|
||||
Cleanup(RetentionTime)
|
||||
Cleanup(RetentionTime),
|
||||
}
|
||||
|
||||
/// The retention time for snapshot and delta files no longer referenced.
|
||||
@@ -128,28 +106,26 @@ impl RrdpCommandDetails {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RrdpServerError -----------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
pub enum RrdpServerError {
|
||||
|
||||
#[display(fmt = "{}", _0)]
|
||||
IoError(io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for RrdpServerError {
|
||||
fn from(e: io::Error) -> Self { RrdpServerError::IoError(e) }
|
||||
fn from(e: io::Error) -> Self {
|
||||
RrdpServerError::IoError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RrdpServerError {}
|
||||
|
||||
|
||||
//------------ RrdpResult ----------------------------------------------------
|
||||
|
||||
pub type RrdpResult = Result<Vec<RrdpEvent>, RrdpServerError>;
|
||||
|
||||
|
||||
//------------ RrdpServer ----------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@@ -169,20 +145,19 @@ pub struct RrdpServer {
|
||||
|
||||
notification: Notification,
|
||||
snapshot: Snapshot,
|
||||
deltas: Vec<Delta>
|
||||
deltas: Vec<Delta>,
|
||||
}
|
||||
|
||||
/// # Publishing
|
||||
///
|
||||
impl RrdpServer {
|
||||
|
||||
fn process_published_delta(&mut self, delta: Delta) {
|
||||
self.snapshot.apply_delta(delta.clone());
|
||||
self.deltas.insert(0, delta);
|
||||
|
||||
// Keep a minimum of 2 deltas, and a maximum for which the combined
|
||||
// number of elements does not exceed the number of elements in the
|
||||
// snapshot.
|
||||
// Keep a minimum of 2 deltas, and a maximum for which the combined
|
||||
// number of elements does not exceed the number of elements in the
|
||||
// snapshot.
|
||||
{
|
||||
let size_snapshot = self.snapshot.len();
|
||||
let mut total_deltas = 0;
|
||||
@@ -205,18 +180,19 @@ impl RrdpServer {
|
||||
let session = self.session.clone();
|
||||
let delta = Delta::new(session, next, elements);
|
||||
|
||||
Ok(vec![RrdpEventDetails::added_delta(&id(), self.version, delta)])
|
||||
Ok(vec![RrdpEventDetails::added_delta(
|
||||
&id(),
|
||||
self.version,
|
||||
delta,
|
||||
)])
|
||||
}
|
||||
|
||||
/// Publishes the latest notification, snapshot and delta file to disk.
|
||||
/// Return event to move old files to clean-up list.
|
||||
fn publish(&self) -> RrdpResult {
|
||||
let snapshot_hash = self.snapshot.write_xml(&self.snapshot_path())?;
|
||||
let snapshot_ref = SnapshotRef::new(
|
||||
self.snapshot_uri(),
|
||||
self.snapshot_path(),
|
||||
snapshot_hash
|
||||
);
|
||||
let snapshot_ref =
|
||||
SnapshotRef::new(self.snapshot_uri(), self.snapshot_path(), snapshot_hash);
|
||||
|
||||
// Note we always have at least 1 delta when publishing.
|
||||
let last_delta = &self.deltas[0];
|
||||
@@ -226,8 +202,8 @@ impl RrdpServer {
|
||||
FileRef::new(
|
||||
self.delta_uri(last_delta.serial()),
|
||||
self.delta_path(last_delta.serial()),
|
||||
delta_hash
|
||||
)
|
||||
delta_hash,
|
||||
),
|
||||
);
|
||||
|
||||
let update = NotificationUpdate::new(
|
||||
@@ -235,20 +211,18 @@ impl RrdpServer {
|
||||
None,
|
||||
snapshot_ref,
|
||||
delta_ref,
|
||||
self.deltas.last().unwrap().serial()
|
||||
self.deltas.last().unwrap().serial(),
|
||||
);
|
||||
|
||||
let mut notification = self.notification.clone();
|
||||
notification.update(update.clone());
|
||||
notification.write_xml(&self.notification_path())?;
|
||||
|
||||
Ok(vec![
|
||||
RrdpEventDetails::updated_notification(
|
||||
&id(),
|
||||
self.version,
|
||||
update
|
||||
)
|
||||
])
|
||||
Ok(vec![RrdpEventDetails::updated_notification(
|
||||
&id(),
|
||||
self.version,
|
||||
update,
|
||||
)])
|
||||
}
|
||||
|
||||
/// Cleans out old files on disk, returns event for cleaning up the state.
|
||||
@@ -261,13 +235,11 @@ impl RrdpServer {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vec![
|
||||
RrdpEventDetails::cleaned_up(
|
||||
&id(),
|
||||
self.version,
|
||||
cut_off
|
||||
)
|
||||
])
|
||||
Ok(vec![RrdpEventDetails::cleaned_up(
|
||||
&id(),
|
||||
self.version,
|
||||
cut_off,
|
||||
)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,9 +247,7 @@ impl RrdpServer {
|
||||
///
|
||||
impl RrdpServer {
|
||||
pub fn notification_uri(&self) -> uri::Https {
|
||||
uri::Https::from_string(
|
||||
format!("{}notifcation.xml", self.base_uri.to_string())
|
||||
).unwrap() // Cannot fail. Config checked at startup.
|
||||
uri::Https::from_string(format!("{}notifcation.xml", self.base_uri.to_string())).unwrap() // Cannot fail. Config checked at startup.
|
||||
}
|
||||
|
||||
fn notification_path(&self) -> PathBuf {
|
||||
@@ -301,12 +271,12 @@ impl RrdpServer {
|
||||
}
|
||||
|
||||
fn new_snapshot_uri(base: &uri::Https, session: &str, serial: u64) -> uri::Https {
|
||||
uri::Https::from_string(
|
||||
format!("{}{}",
|
||||
base.to_string(),
|
||||
Self::snapshot_rel(session, serial)
|
||||
)
|
||||
).unwrap() // Cannot fail. Config checked at startup.
|
||||
uri::Https::from_string(format!(
|
||||
"{}{}",
|
||||
base.to_string(),
|
||||
Self::snapshot_rel(session, serial)
|
||||
))
|
||||
.unwrap() // Cannot fail. Config checked at startup.
|
||||
}
|
||||
|
||||
fn snapshot_uri(&self) -> uri::Https {
|
||||
@@ -318,12 +288,12 @@ impl RrdpServer {
|
||||
}
|
||||
|
||||
fn delta_uri(&self, serial: u64) -> uri::Https {
|
||||
uri::Https::from_string(
|
||||
format!("{}{}",
|
||||
self.base_uri.to_string(),
|
||||
Self::delta_rel(&self.session, serial)
|
||||
)
|
||||
).unwrap() // Cannot fail. Config checked at startup.
|
||||
uri::Https::from_string(format!(
|
||||
"{}{}",
|
||||
self.base_uri.to_string(),
|
||||
Self::delta_rel(&self.session, serial)
|
||||
))
|
||||
.unwrap() // Cannot fail. Config checked at startup.
|
||||
}
|
||||
|
||||
fn delta_path(&self, serial: u64) -> PathBuf {
|
||||
@@ -333,8 +303,6 @@ impl RrdpServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Aggregate for RrdpServer {
|
||||
type Command = RrdpCommand;
|
||||
type Event = RrdpEvent;
|
||||
@@ -356,11 +324,7 @@ impl Aggregate for RrdpServer {
|
||||
let snapshot_uri = Self::new_snapshot_uri(&base_uri, &session, 0);
|
||||
let snapshot_hash = snapshot.write_xml(&snapshot_path)?;
|
||||
|
||||
let snapshot_ref = SnapshotRef::new(
|
||||
snapshot_uri,
|
||||
snapshot_path,
|
||||
snapshot_hash
|
||||
);
|
||||
let snapshot_ref = SnapshotRef::new(snapshot_uri, snapshot_path, snapshot_hash);
|
||||
|
||||
let notification = Notification::create(session.clone(), snapshot_ref);
|
||||
let deltas = vec![];
|
||||
@@ -373,7 +337,7 @@ impl Aggregate for RrdpServer {
|
||||
serial,
|
||||
notification,
|
||||
snapshot,
|
||||
deltas
|
||||
deltas,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -383,15 +347,13 @@ impl Aggregate for RrdpServer {
|
||||
|
||||
fn apply(&mut self, event: Self::Event) {
|
||||
match event.into_details() {
|
||||
RrdpEventDetails::AddedDelta(delta) =>
|
||||
self.process_published_delta(delta),
|
||||
RrdpEventDetails::UpdatedNotification(notification) =>
|
||||
self.notification.update(notification),
|
||||
RrdpEventDetails::CleanedUp(time) =>
|
||||
self.notification.clean_up(time)
|
||||
RrdpEventDetails::AddedDelta(delta) => self.process_published_delta(delta),
|
||||
RrdpEventDetails::UpdatedNotification(notification) => {
|
||||
self.notification.update(notification)
|
||||
}
|
||||
RrdpEventDetails::CleanedUp(time) => self.notification.clean_up(time),
|
||||
}
|
||||
self.version += 1;
|
||||
|
||||
}
|
||||
|
||||
fn process_command(&self, command: Self::Command) -> RrdpResult {
|
||||
@@ -403,7 +365,6 @@ impl Aggregate for RrdpServer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ RsyncdStore ---------------------------------------------------
|
||||
|
||||
/// This type is responsible for publishing files on disk in a structure so
|
||||
@@ -413,7 +374,7 @@ impl Aggregate for RrdpServer {
|
||||
/// base uri used.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RsyncdStore {
|
||||
rsync_dir: PathBuf
|
||||
rsync_dir: PathBuf,
|
||||
}
|
||||
|
||||
/// # Construct
|
||||
@@ -422,10 +383,10 @@ impl RsyncdStore {
|
||||
pub fn build(repo_dir: &PathBuf) -> Result<Self, io::Error> {
|
||||
let mut rsync_dir = PathBuf::from(repo_dir);
|
||||
rsync_dir.push(RSYNC_FOLDER);
|
||||
if ! rsync_dir.is_dir() {
|
||||
if !rsync_dir.is_dir() {
|
||||
fs::create_dir_all(&rsync_dir)?;
|
||||
}
|
||||
Ok ( RsyncdStore { rsync_dir } )
|
||||
Ok(RsyncdStore { rsync_dir })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,29 +395,17 @@ impl RsyncdStore {
|
||||
impl RsyncdStore {
|
||||
/// Saves all the publishes and updates, deletes all the withdraws.
|
||||
pub fn publish(&self, delta: &DeltaElements) -> Result<(), io::Error> {
|
||||
|
||||
for p in delta.publishes() {
|
||||
file::save_with_rsync_uri(
|
||||
&p.base64().to_bytes(),
|
||||
&self.rsync_dir,
|
||||
p.uri()
|
||||
)?;
|
||||
file::save_with_rsync_uri(&p.base64().to_bytes(), &self.rsync_dir, p.uri())?;
|
||||
}
|
||||
|
||||
for u in delta.updates() {
|
||||
file::save_with_rsync_uri(
|
||||
&u.base64().to_bytes(),
|
||||
&self.rsync_dir,
|
||||
u.uri()
|
||||
)?;
|
||||
file::save_with_rsync_uri(&u.base64().to_bytes(), &self.rsync_dir, u.uri())?;
|
||||
}
|
||||
|
||||
for w in delta.withdraws() {
|
||||
file::delete_with_rsync_uri(
|
||||
&self.rsync_dir,
|
||||
w.uri()
|
||||
)?;
|
||||
file::delete_with_rsync_uri(&self.rsync_dir, w.uri())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user