This commit is contained in:
Tim Bruijnzeels
2019-07-30 13:58:48 +02:00
parent 73a525c9b1
commit cd2e7133e9
70 changed files with 4327 additions and 5780 deletions
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+56 -59
View File
@@ -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
View File
@@ -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
}
}
+78 -51
View File
@@ -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
View File
@@ -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)
}
}
+2 -8
View File
@@ -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>;
}
}
+32 -47
View File
@@ -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)
}
}
}
+12 -8
View File
@@ -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
+13 -7
View File
@@ -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
}
}
}
+5 -4
View File
@@ -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 }),
}
}
}
+41 -58
View File
@@ -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())
})
}
}
}
+34 -68
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
});
}
}
+54 -59
View File
@@ -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
View File
@@ -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();
}
}
+2 -2
View File
@@ -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
View File
@@ -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 {
});
}
}
}
+12 -27
View File
@@ -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 {
});
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+161 -204
View File
@@ -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);
}
}
+41 -86
View File
@@ -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();
}
}
+12 -12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)))
}
}
+48 -79
View File
@@ -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();
+14 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+23 -64
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 -1
View File
@@ -1,3 +1,3 @@
pub mod server;
pub mod ssl;
pub mod statics;
pub mod statics;
+49 -86
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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),
}
}
}
+17 -31
View File
@@ -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
View File
@@ -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)
};
+54 -96
View File
@@ -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());
});
}
}
+6 -7
View File
@@ -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
View File
@@ -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.
+2 -4
View File
@@ -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);
}
}
}
}
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}
}
}