diff --git a/defaults/server.toml b/defaults/server.toml
index e699c345..4ddc21f9 100644
--- a/defaults/server.toml
+++ b/defaults/server.toml
@@ -21,5 +21,5 @@ rsync_base = "rsync://127.0.0.1/repo/"
# in their certificates.
notify_sia = "https://127.0.0.1/repo/notify.xml"
-# Specify the service URI where publishers can connect.
-service_uri = "https://127.0.0.1/publish"
\ No newline at end of file
+# Specify the base service URI where publishers can connect.
+service_uri = "https://127.0.0.1/rfc8181/"
\ No newline at end of file
diff --git a/src/pubd/daemon.rs b/src/pubd/daemon.rs
index 68c363ad..5a8d5ff7 100644
--- a/src/pubd/daemon.rs
+++ b/src/pubd/daemon.rs
@@ -2,6 +2,7 @@ extern crate hyper;
extern crate futures;
use self::hyper::{Body, Method, Response, Server, StatusCode};
+use self::hyper::Request;
use self::hyper::rt::Future;
use self::hyper::service::service_fn_ok;
use pubd::config::Config;
@@ -34,20 +35,24 @@ pub fn serve(config: &Config) {
let pub_server = pub_server.clone();
- service_fn_ok(move |req| {
- let path = req.uri().path();
+ service_fn_ok(move |req: Request
| {
+ let (parts, _body) = req.into_parts();
+ let path = parts.uri.path();
- if path.starts_with("/static") {
+ if path.starts_with("/rfc8181/") {
+ let _handle = path.trim_left_matches("/publishers/");
+ unimplemented!()
+ } else if path.starts_with("/static") {
render_static(path)
} else if path.starts_with("/publishers/") {
let handle = path.trim_left_matches("/publishers/");
show_repository_response(handle, &pub_server)
} else {
- match (req.method(), path) {
- (&Method::GET, "/health") => {
+ match (parts.method, path) {
+ (Method::GET, "/health") => {
service_ok()
},
- (&Method::GET, "/publishers") => {
+ (Method::GET, "/publishers") => {
show_publishers(&pub_server)
},
_ => {
@@ -131,6 +136,7 @@ fn show_repository_response(
}
+//------------ Error ---------------------------------------------------------
#[derive(Debug, Fail)]
pub enum Error {
diff --git a/src/pubd/responder.rs b/src/pubd/responder.rs
index 91cc748a..6898718d 100644
--- a/src/pubd/responder.rs
+++ b/src/pubd/responder.rs
@@ -2,11 +2,19 @@ use std::io;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
+use bcder::encode::Values;
+use bcder::Mode;
+use bcder::Captured;
+use provisioning::publisher::Publisher;
+use rpki::oob::exchange::RepositoryResponse;
+use rpki::publication::pubmsg::Message;
use rpki::signing::builder::IdCertBuilder;
+use rpki::signing::builder::SignedMessageBuilder;
use rpki::signing::PublicKeyAlgorithm;
use rpki::signing::signer::Signer;
use rpki::signing::signer::CreateKeyError;
use rpki::signing::signer::KeyUseError;
+use rpki::uri;
use signing::identity::MyIdentity;
use signing::softsigner;
use signing::softsigner::OpenSslSigner;
@@ -15,9 +23,6 @@ use storage::keystore;
use storage::keystore::Key;
use storage::keystore::KeyStore;
use storage::keystore::Info;
-use provisioning::publisher::Publisher;
-use rpki::oob::exchange::RepositoryResponse;
-use rpki::uri;
/// # Naming things in the keystore.
@@ -141,6 +146,21 @@ impl Responder {
}
}
+ /// Creates an encoded SignedMessage for a contained Message.
+ pub fn sign_msg(&mut self, msg: Message) -> Result {
+ if let Some(id) = self.my_identity()? {
+ let builder = SignedMessageBuilder::new(
+ id.key_id(),
+ &mut self.signer,
+ msg
+ )?;
+ let enc = builder.encode().to_captured(Mode::Der);
+ Ok(enc)
+ } else {
+ Err(Error::Unitialised)
+ }
+ }
+
}
diff --git a/src/pubd/server.rs b/src/pubd/server.rs
index 427573d5..17cc2b89 100644
--- a/src/pubd/server.rs
+++ b/src/pubd/server.rs
@@ -2,14 +2,18 @@
use std::path::PathBuf;
use std::sync::Arc;
+use bcder::Captured;
use provisioning::publisher::Publisher;
-use provisioning::publisher_store;
-use provisioning::publisher_store::PublisherStore;
+use provisioning::publisher_store::{self, PublisherStore};
+use pubd::responder::{self, Responder};
+use repo::file_store;
use repo::repository::{self, Repository};
-use rpki::uri;
use rpki::oob::exchange::RepositoryResponse;
-use pubd::responder::Responder;
-use pubd::responder;
+use rpki::publication::pubmsg::{Message, MessageError, QueryMessage};
+use rpki::publication::reply::{ErrorReply, ReportError, ReportErrorCode};
+use rpki::remote::sigmsg::SignedMessage;
+use rpki::uri;
+use rpki::x509::ValidationError;
/// # Naming things in the keystore.
@@ -102,10 +106,93 @@ impl PubServer {
}
/// # Handle publisher requests
+///
impl PubServer {
+ /// Handles an incoming SignedMessage, verifies it's validly signed by
+ /// a known publisher and process the QueryMessage contained. Returns
+ /// a signed response to the publisher.
+ ///
+ /// Note this returns an error for cases where we do not want to do any
+ /// work in signing, like the publisher does not exist, or the
+ /// signature is invalid. The daemon will need to map these to HTTTP
+ /// codes.
+ ///
+ /// Also note that if garbage is sent to the daemon, this garbage will
+ /// fail to parse as a SignedMessage, and the daemon will just respond
+ /// with an HTTP error response, without invoking any of this.
+ pub fn handle_request(
+ &mut self,
+ sigmsg: SignedMessage,
+ publisher_handle: &str
+ ) -> Result {
+ let publisher = self.publisher_store.get_publisher(publisher_handle)?;
+ let base_uri = publisher.base_uri();
+ sigmsg.validate(publisher.id_cert())?;
+
+ let res_msg = match Message::from_signed_message(&sigmsg) {
+ Ok(msg) => {
+ match msg.as_query() {
+ Ok(query) => self.handle_query(&query, base_uri),
+ Err(e) => Self::build_error(e)
+ }
+ },
+ Err(e) => {
+ Self::build_error(e)
+ }
+ };
+
+ let sigres = self.responder.sign_msg(res_msg)?;
+ Ok(sigres)
+ }
+
+
+ /// Handles a publish or list query for a publisher. Needs to know the
+ /// base_uri for the publisher to enforce constraints, but can assume
+ /// that the PubServer has already validated the incoming SignedMessage.
+ ///
+ /// Returns the appropriate Success or Error Reply in a Message, ready
+ /// for wrapping into a SignedMessage.
+ fn handle_query(
+ &self,
+ query: &QueryMessage,
+ base_uri: &uri::Rsync
+ ) -> Message {
+ match query {
+ QueryMessage::PublishQuery(publish) => {
+ match self.repository.publish(publish, base_uri) {
+ Err(e) => {
+ Self::build_error(e)
+ },
+ Ok(success) => success
+ }
+ },
+ QueryMessage::ListQuery(_list) => {
+ match self.repository.list(base_uri) {
+ Err(e) => {
+ Self::build_error(e)
+ },
+ Ok(success) => success
+ }
+ }
+ }
+ }
+
+ fn build_error(error: impl ToReportErrorCode) -> Message {
+ let mut error_builder = ErrorReply::build();
+ error_builder.add(
+ ReportError::reply(
+ error.to_report_error_code(),
+ None // Finding the specific PDU is too much hard work.
+ )
+ );
+ error_builder.build_message()
+ }
}
+
+
+
//------------ Error ---------------------------------------------------------
#[derive(Debug, Fail)]
@@ -118,6 +205,12 @@ pub enum Error {
#[fail(display="{}", _0)]
RepositoryError(repository::Error),
+
+ #[fail(display="{}", _0)]
+ MessageError(MessageError),
+
+ #[fail(display="{}", _0)]
+ ValdiationError(ValidationError),
}
impl From for Error {
@@ -138,6 +231,58 @@ impl From for Error {
}
}
+impl From for Error {
+ fn from(e: MessageError) -> Self {
+ Error::MessageError(e)
+ }
+}
+
+impl From for Error {
+ fn from(e: ValidationError) -> Self {
+ Error::ValdiationError(e)
+ }
+}
+
+
+//------------ ToReportErrorCode ---------------------------------------------
+
+trait ToReportErrorCode {
+ fn to_report_error_code(&self) -> ReportErrorCode;
+}
+
+impl ToReportErrorCode for MessageError {
+ fn to_report_error_code(&self) -> ReportErrorCode {
+ ReportErrorCode::XmlError
+ }
+}
+
+impl ToReportErrorCode for repository::Error {
+ fn to_report_error_code(&self) -> ReportErrorCode {
+ match self {
+ repository::Error::FileStoreError(error) =>
+ error.to_report_error_code()
+ }
+ }
+}
+
+impl ToReportErrorCode for file_store::Error {
+ fn to_report_error_code(&self) -> ReportErrorCode {
+ match self {
+ file_store::Error::ObjectAlreadyPresent(_) =>
+ ReportErrorCode::ObjectAlreadyPresent,
+ file_store::Error::NoObjectPresent(_) =>
+ ReportErrorCode::NoObjectPresent,
+ file_store::Error::NoObjectMatchingHash =>
+ ReportErrorCode::NoObjectMatchingHash,
+ file_store::Error::OutsideBaseUri =>
+ ReportErrorCode::PermissionFailure,
+ _ => ReportErrorCode::OtherError
+ }
+ }
+}
+
+
+
//------------ Tests ---------------------------------------------------------
#[cfg(test)]
@@ -267,8 +412,7 @@ mod tests {
}
#[test]
- fn
- should_initialise_publishers_from_xml_and_have_response() {
+ fn should_initialise_publishers_from_xml_and_have_response() {
test::test_with_tmp_dir(|d| {
let xml_dir = test::create_sub_dir(&d);
diff --git a/src/repo/file_store.rs b/src/repo/file_store.rs
index 4cddf6d6..2adbdebb 100644
--- a/src/repo/file_store.rs
+++ b/src/repo/file_store.rs
@@ -31,7 +31,7 @@ impl FileStore {
///
impl FileStore {
/// Process a PublishQuery update
- pub fn update(
+ pub fn publish(
&self,
update: &PublishQuery,
base_uri: &uri::Rsync
@@ -85,27 +85,27 @@ impl FileStore {
PublishElement::Publish(p) => {
Self::assert_uri(base_uri, p.uri())?;
if self.get_current_file_opt(p.uri())?.is_some() {
- return Err(Error::PublishWrongUri(p.uri().clone()))
+ return Err(Error::ObjectAlreadyPresent(p.uri().clone()))
}
},
PublishElement::Update(u) => {
Self::assert_uri(base_uri, u.uri())?;
if let Some(cur) = self.get_current_file_opt(u.uri())? {
if cur.hash() != u.hash() {
- return Err(Error::UpdateWrongHash)
+ return Err(Error::NoObjectMatchingHash)
}
} else {
- return Err(Error::UpdateWrongUri(u.uri().clone()))
+ return Err(Error::NoObjectPresent(u.uri().clone()))
}
},
PublishElement::Withdraw(w) => {
Self::assert_uri(base_uri, w.uri())?;
if let Some(cur) = self.get_current_file_opt(w.uri())? {
if cur.hash() != w.hash() {
- return Err(Error::WithdrawWrongHash)
+ return Err(Error::NoObjectMatchingHash)
}
} else {
- return Err(Error::UpdateWrongUri(w.uri().clone()))
+ return Err(Error::NoObjectPresent(w.uri().clone()))
}
},
}
@@ -235,19 +235,13 @@ pub enum Error {
UriError(uri::Error),
#[fail(display="File already exists for uri (use update!): {}", _0)]
- PublishWrongUri(uri::Rsync),
+ ObjectAlreadyPresent(uri::Rsync),
- #[fail(display="File sent for update has no entry for uri: {}", _0)]
- UpdateWrongUri(uri::Rsync),
+ #[fail(display="Np file present for uri: {}", _0)]
+ NoObjectPresent(uri::Rsync),
- #[fail(display="File for update exists, but hash does not match")]
- UpdateWrongHash,
-
- #[fail(display="The withdraw URI is not known: {}", _0)]
- WithdrawWrongUri(uri::Rsync),
-
- #[fail(display="File for withdraw exists, but hash does not match")]
- WithdrawWrongHash,
+ #[fail(display="File does not match hash")]
+ NoObjectMatchingHash,
#[fail(display="Publishing outside of base URI is not allowed.")]
OutsideBaseUri,
@@ -294,7 +288,7 @@ mod tests {
let message = builder.build_message();
let publish = message.as_query().unwrap().as_publish().unwrap();
- file_store.update(&publish, &base_uri).unwrap();
+ file_store.publish(&publish, &base_uri).unwrap();
// See that it's the only one listed
let files = file_store.list(&base_uri).unwrap();
@@ -311,7 +305,7 @@ mod tests {
builder.add(file_update.clone().as_update(file.content()));
let message = builder.build_message();
let publish = message.as_query().unwrap().as_publish().unwrap();
- file_store.update(&publish, &base_uri).unwrap();
+ file_store.publish(&publish, &base_uri).unwrap();
// See that it's the only one listed
let files = file_store.list(&base_uri).unwrap();
@@ -323,7 +317,7 @@ mod tests {
builder.add(file_update.as_withdraw());
let message = builder.build_message();
let publish = message.as_query().unwrap().as_publish().unwrap();
- file_store.update(&publish, &base_uri).unwrap();
+ file_store.publish(&publish, &base_uri).unwrap();
// See that there are no files listed
let files = file_store.list(&base_uri).unwrap();
@@ -352,7 +346,7 @@ mod tests {
let message = builder.build_message();
let publish = message.as_query().unwrap().as_publish().unwrap();
- match file_store.update(&publish, &base_uri) {
+ match file_store.publish(&publish, &base_uri) {
Err(Error::OutsideBaseUri) => {},
_ => { panic!("Expected Error::OutsideBaseUri") }
}
diff --git a/src/repo/mod.rs b/src/repo/mod.rs
index d6e8126c..c14c0359 100644
--- a/src/repo/mod.rs
+++ b/src/repo/mod.rs
@@ -1,4 +1,4 @@
mod file;
-mod file_store;
+pub mod file_store;
pub mod repository;
\ No newline at end of file
diff --git a/src/repo/repository.rs b/src/repo/repository.rs
index 70b53783..3430717b 100644
--- a/src/repo/repository.rs
+++ b/src/repo/repository.rs
@@ -1,5 +1,11 @@
use std::path::PathBuf;
use repo::file_store::{self, FileStore};
+use rpki::publication::pubmsg::Message;
+use rpki::publication::query::PublishQuery;
+use rpki::publication::reply::ListElement;
+use rpki::publication::reply::ListReply;
+use rpki::publication::reply::SuccessReply;
+use rpki::uri;
//------------ Repository ----------------------------------------------------
@@ -23,6 +29,41 @@ impl Repository {
}
}
+/// # Publish / List
+///
+impl Repository {
+ /// Publishes an publish query and returns a success reply embedded in
+ /// a message. Throws an error in case of issues. The PubServer needs
+ /// to wrap such errors in a response message to the publisher.
+ pub fn publish(
+ &self,
+ update: &PublishQuery,
+ base_uri: &uri::Rsync
+ ) -> Result {
+ self.fs.publish(update, base_uri)?;
+ Ok(SuccessReply::build_message())
+ }
+
+ /// Lists the objects for a base_uri, presumably all for the same
+ /// publisher.
+ pub fn list(
+ &self,
+ base_uri: &uri::Rsync
+ ) -> Result {
+ let files = self.fs.list(base_uri)?;
+ let mut builder = ListReply::build();
+ for file in files {
+ builder.add(
+ ListElement::reply(
+ file.content(),
+ file.uri().clone()
+ )
+ )
+ }
+ Ok(builder.build_message())
+ }
+}
+
//------------ Error ---------------------------------------------------------
@@ -36,4 +77,4 @@ impl From for Error {
fn from(e: file_store::Error) -> Self {
Error::FileStoreError(e)
}
-}
+}
\ No newline at end of file