mirror of
https://github.com/NLnetLabs/krill.git
synced 2026-09-17 23:17:42 +02:00
Some more work done to separate the CMS/XML protocol and JSON. Still some more cleanup coming. (see issue #34)
This commit is contained in:
@@ -28,6 +28,7 @@ serde_json = "^1.0"
|
||||
syslog = "^4.0"
|
||||
toml = "^0.4"
|
||||
tokio = "^0.1"
|
||||
uuid = "0.7"
|
||||
xml-rs = "0.8.0"
|
||||
|
||||
# XXX Temporarily
|
||||
|
||||
@@ -34,9 +34,6 @@
|
||||
# in their certificates.
|
||||
#rrdp_base_uri = "http://127.0.0.1:3000/rrdp/"
|
||||
|
||||
# Specify the base service URI where publishers can connect.
|
||||
#service_uri = "http://127.0.0.1:3000/rfc8181/"
|
||||
|
||||
# Log level
|
||||
#
|
||||
# The maximum log level ("off", "error", "warn", "info", or "debug") for
|
||||
|
||||
+45
-18
@@ -163,6 +163,23 @@ impl PublisherSummary {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Rfc8181Details ------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Rfc8181Details {
|
||||
#[serde(
|
||||
serialize_with = "ext_serde::ser_http_uri",
|
||||
deserialize_with = "ext_serde::de_http_uri")]
|
||||
service_uri: uri::Http,
|
||||
|
||||
#[serde(
|
||||
serialize_with = "ext_serde::ser_id_cert",
|
||||
deserialize_with = "ext_serde::de_id_cert")]
|
||||
id_cert: IdCert
|
||||
}
|
||||
|
||||
|
||||
//------------ PublisherDetails ----------------------------------------------
|
||||
|
||||
/// This type defines the publisher details fro:
|
||||
@@ -177,16 +194,7 @@ pub struct PublisherDetails {
|
||||
)]
|
||||
base_uri: uri::Rsync,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_http_uri",
|
||||
serialize_with = "ext_serde::ser_http_uri"
|
||||
)]
|
||||
service_uri: uri::Http,
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_id_cert",
|
||||
serialize_with = "ext_serde::ser_id_cert"
|
||||
)]
|
||||
identity_certificate: IdCert,
|
||||
rfc8181: Option<Rfc8181Details>,
|
||||
|
||||
links: Vec<Link>
|
||||
}
|
||||
@@ -195,8 +203,11 @@ impl PublisherDetails {
|
||||
pub fn publisher_handle(&self) -> &str {
|
||||
&self.publisher_handle
|
||||
}
|
||||
pub fn identity_cert(&self) -> &IdCert {
|
||||
&self.identity_certificate
|
||||
pub fn identity_cert(&self) -> Option<&IdCert> {
|
||||
match self.rfc8181 {
|
||||
None => None,
|
||||
Some(ref details) => Some(&details.id_cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,12 +230,28 @@ impl Report for PublisherDetails {
|
||||
},
|
||||
ReportFormat::Text => {
|
||||
|
||||
let res = format!(
|
||||
"publisher_handle: {}\n\
|
||||
base uri: {}\n\
|
||||
service_uri: {}",
|
||||
self.publisher_handle, self.base_uri, self.service_uri
|
||||
);
|
||||
let mut res = String::new();
|
||||
|
||||
res.push_str("handle: ");
|
||||
res.push_str(self.publisher_handle.as_str());
|
||||
res.push_str("\n");
|
||||
|
||||
res.push_str("base uri: ");
|
||||
res.push_str(self.base_uri.to_string().as_str());
|
||||
res.push_str("\n");
|
||||
|
||||
if let Some(ref rfc8181) = self.rfc8181 {
|
||||
res.push_str("RFC8181 Details:\n");
|
||||
res.push_str(" service uri: ");
|
||||
res.push_str(rfc8181.service_uri.to_string().as_str());
|
||||
res.push_str("\n");
|
||||
res.push_str(" id cert (base64): ");
|
||||
res.push_str(
|
||||
base64::encode(
|
||||
rfc8181.id_cert.to_bytes().as_ref()
|
||||
).as_str());
|
||||
res.push_str("\n");
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
|
||||
+12
-3
@@ -113,9 +113,15 @@ impl KrillClient {
|
||||
let uri = format!("api/v1/publishers/{}", handle);
|
||||
let res = self.get(uri.as_str())?;
|
||||
let details: PublisherDetails = serde_json::from_str(&res)?;
|
||||
let bytes = details.identity_cert().to_bytes();
|
||||
file::save(&bytes, &file)?;
|
||||
Ok(ApiResponse::Empty)
|
||||
match details.identity_cert() {
|
||||
Some(cert) => {
|
||||
let bytes = cert.to_bytes();
|
||||
file::save(&bytes, &file)?;
|
||||
Ok(ApiResponse::Empty)
|
||||
},
|
||||
None => Err(Error::NoIdCert)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,6 +277,9 @@ pub enum Error {
|
||||
|
||||
#[display(fmt="Can't read file: {}", _0)]
|
||||
IoError(io::Error),
|
||||
|
||||
#[display(fmt="There is no known IdCert for this publisher")]
|
||||
NoIdCert
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for Error {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Support for various admin API methods
|
||||
//! Process requests received, delegate, and wrap up the responses.
|
||||
use std::error;
|
||||
use std::sync::{RwLockReadGuard, RwLockWriteGuard};
|
||||
use actix_web::{HttpResponse, ResponseError};
|
||||
@@ -8,7 +8,22 @@ use crate::daemon::api::responses::{PublisherDetails, PublisherList};
|
||||
use crate::daemon::http::server::{HttpRequest, PublisherHandle};
|
||||
use crate::daemon::publishers;
|
||||
use crate::daemon::krillserver::{self, KrillServer};
|
||||
use crate::remote::rfc8183::PublisherRequest;
|
||||
use remote::sigmsg::SignedMessage;
|
||||
use daemon::api::requests::PublishDelta;
|
||||
use daemon::api::requests::PublisherRequestChoice;
|
||||
|
||||
|
||||
//------------ Support Functions ---------------------------------------------
|
||||
|
||||
/// Returns a server in a read lock
|
||||
pub fn ro_server(req: &HttpRequest) -> RwLockReadGuard<KrillServer> {
|
||||
req.state().read().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a server in a write lock
|
||||
pub fn rw_server(req: &HttpRequest) -> RwLockWriteGuard<KrillServer> {
|
||||
req.state().write().unwrap()
|
||||
}
|
||||
|
||||
/// Helper function to render json output.
|
||||
fn render_json<O: Serialize>(object: O) -> HttpResponse {
|
||||
@@ -29,26 +44,24 @@ fn server_error(error: Error) -> HttpResponse {
|
||||
error.error_response()
|
||||
}
|
||||
|
||||
/// Returns a server in a read lock
|
||||
fn ro_server(req: &HttpRequest) -> RwLockReadGuard<KrillServer> {
|
||||
req.state().read().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a server in a write lock
|
||||
fn rw_server(req: &HttpRequest) -> RwLockWriteGuard<KrillServer> {
|
||||
req.state().write().unwrap()
|
||||
}
|
||||
|
||||
/// A clean 404 result for the API (no content, not for humans)
|
||||
fn api_not_found() -> HttpResponse {
|
||||
HttpResponse::build(StatusCode::NOT_FOUND).finish()
|
||||
}
|
||||
|
||||
/// A clean 200 result for the API (no content, not for humans)
|
||||
fn api_ok() -> HttpResponse {
|
||||
pub fn api_ok() -> HttpResponse {
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
|
||||
/// Returns the server health. XXX TODO: do a real test!
|
||||
pub fn health(_r: &HttpRequest) -> HttpResponse {
|
||||
api_ok()
|
||||
}
|
||||
|
||||
|
||||
//------------ Admin: Publishers ---------------------------------------------
|
||||
|
||||
/// Returns a json structure with all publishers in it.
|
||||
pub fn publishers(req: &HttpRequest) -> HttpResponse {
|
||||
match ro_server(req).publishers() {
|
||||
@@ -65,23 +78,23 @@ pub fn publishers(req: &HttpRequest) -> HttpResponse {
|
||||
/// Request XML is posted.
|
||||
pub fn add_publisher(
|
||||
req: HttpRequest,
|
||||
pr: PublisherRequest
|
||||
prc: PublisherRequestChoice
|
||||
) -> HttpResponse {
|
||||
let mut server = rw_server(&req);
|
||||
let handle = pr.handle().clone();
|
||||
match server.add_publisher(pr, &handle) {
|
||||
match server.add_publisher(prc, None) {
|
||||
Ok(()) => api_ok(),
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a an explicitly named publisher.
|
||||
pub fn add_named_publisher(
|
||||
req: HttpRequest,
|
||||
pr: PublisherRequest,
|
||||
prc: PublisherRequestChoice,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
let mut server = rw_server(&req);
|
||||
match server.add_publisher(pr, handle.as_ref()) {
|
||||
match server.add_publisher(prc, Some(handle.as_ref())) {
|
||||
Ok(()) => api_ok(),
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
@@ -101,41 +114,26 @@ pub fn remove_publisher(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns a json structure with publisher details
|
||||
pub fn publisher_details(
|
||||
req: HttpRequest,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
match ro_server(&req).publisher(handle) {
|
||||
let server = ro_server(&req);
|
||||
match server.publisher(handle) {
|
||||
Ok(None) => api_not_found(),
|
||||
Ok(Some(publisher)) => {
|
||||
render_json(
|
||||
PublisherDetails::from(&publisher, "/api/v1/publishers")
|
||||
PublisherDetails::from(
|
||||
&publisher,
|
||||
"/api/v1/publishers",
|
||||
server.service_base_uri())
|
||||
)
|
||||
},
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns the id.cer for a publisher
|
||||
pub fn id_cert(
|
||||
req: HttpRequest,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
match ro_server(&req).publisher(handle) {
|
||||
Ok(None) => api_not_found(),
|
||||
Ok(Some(publisher)) => {
|
||||
let bytes = publisher.id_cert().to_bytes();
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/pkix-cert")
|
||||
.body(bytes)
|
||||
},
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the server's RFC8183 section 5.2.4 Repository Response XML
|
||||
/// file for a known publisher.
|
||||
pub fn repository_response(
|
||||
@@ -158,6 +156,55 @@ pub fn repository_response(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ Publication ---------------------------------------------------
|
||||
|
||||
/// Processes an RFC8181 query and returns the appropriate response.
|
||||
pub fn handle_rfc8181_request(
|
||||
req: HttpRequest,
|
||||
msg: SignedMessage,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
let mut server: RwLockWriteGuard<KrillServer> = rw_server(&req);
|
||||
match server.handle_rfc8181_request(&msg, handle.as_ref()) {
|
||||
Ok(captured) => {
|
||||
HttpResponse::build(StatusCode::OK)
|
||||
.content_type("application/rpki-publication")
|
||||
.body(captured.into_bytes())
|
||||
}
|
||||
Err(e) => {
|
||||
server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a publishdelta request sent to the API.
|
||||
pub fn handle_delta(
|
||||
req: HttpRequest,
|
||||
delta: PublishDelta,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
match rw_server(&req).handle_delta(delta, handle.as_ref()) {
|
||||
Ok(()) => api_ok(),
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a list request sent to the API.
|
||||
pub fn handle_list(
|
||||
req: HttpRequest,
|
||||
handle: PublisherHandle
|
||||
) -> HttpResponse {
|
||||
match ro_server(&req).handle_list(handle.as_ref()) {
|
||||
Ok(list) => render_json(list),
|
||||
Err(e) => server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------------ Error ---------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@@ -214,6 +261,7 @@ impl ErrorToStatus for krillserver::Error {
|
||||
krillserver::Error::CmsProxy(_) => StatusCode::BAD_REQUEST,
|
||||
krillserver::Error::PublisherStore(e) => e.status(),
|
||||
krillserver::Error::Repository(_) => StatusCode::BAD_REQUEST,
|
||||
krillserver::Error::NoIdCert => StatusCode::FORBIDDEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +272,8 @@ impl ErrorToCode for krillserver::Error {
|
||||
krillserver::Error::PublisherStore(e) => e.code(),
|
||||
krillserver::Error::Repository(_) => 3002,
|
||||
krillserver::Error::CmsProxy(_) => 3003,
|
||||
krillserver::Error::NoIdCert => 2001,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +303,6 @@ impl ErrorToStatus for publishers::Error {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct ErrorResponse {
|
||||
code: usize,
|
||||
@@ -269,7 +318,6 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl actix_web::ResponseError for Error {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::build(self.status())
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod admin;
|
||||
pub mod endpoints;
|
||||
pub mod auth;
|
||||
pub mod responses;
|
||||
pub mod requests;
|
||||
@@ -1,18 +1,45 @@
|
||||
//! Support for requests sent to the Json API
|
||||
//!
|
||||
//! So, contrary to the responses, we need to deal with actual values here
|
||||
//! when we deserialize things being sent on the wire.
|
||||
use bytes::Bytes;
|
||||
use rpki::uri;
|
||||
use uuid::Uuid;
|
||||
use crate::remote::rfc8183;
|
||||
use crate::util::ext_serde;
|
||||
|
||||
/// Auto-generate a token in case none is supplied.
|
||||
pub fn generate_random_token() -> String {
|
||||
Uuid::new_v4().to_string()
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct PublisherRequest {
|
||||
handle: String,
|
||||
|
||||
#[serde(default = "generate_random_token")]
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl PublisherRequest {
|
||||
pub fn parts(self) -> (String, String) { (self.handle, self.token) }
|
||||
}
|
||||
|
||||
|
||||
/// This type provides a convenience wrapper so that either XML (rfc81838) or
|
||||
/// Json (our CMS-less api) bodies may be sent when a publisher is added.
|
||||
/// Dependent on the content the body sent this will be converted into the
|
||||
/// right type.
|
||||
pub enum PublisherRequestChoice {
|
||||
Api(PublisherRequest),
|
||||
Rfc8183(rfc8183::PublisherRequest)
|
||||
}
|
||||
|
||||
|
||||
/// This type provides a convenience wrapper to contain the request found
|
||||
/// inside of a validated RFC8181 request.
|
||||
pub enum PublishRequest {
|
||||
List,
|
||||
Delta(PublishDelta)
|
||||
}
|
||||
|
||||
|
||||
/// This type represents the request containing the complete delta of objects
|
||||
/// to publish, update, or withdraw.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
||||
+31
-13
@@ -5,9 +5,9 @@
|
||||
use std::sync::Arc;
|
||||
use rpki::uri;
|
||||
use crate::daemon::publishers::Publisher;
|
||||
use crate::remote::id::IdCert;
|
||||
use crate::util::ext_serde;
|
||||
use crate::util::file::CurrentFile;
|
||||
use crate::remote::id::IdCert;
|
||||
|
||||
//------------ Link ----------------------------------------------------------
|
||||
|
||||
@@ -35,7 +35,7 @@ impl<'a> PublisherSummaryInfo<'a> {
|
||||
publisher: &'a Publisher,
|
||||
path_publishers: &'a str
|
||||
) -> PublisherSummaryInfo<'a> {
|
||||
let id = publisher.name().as_str();
|
||||
let id = publisher.handle().as_str();
|
||||
let mut links = Vec::new();
|
||||
|
||||
let response_link = Link {
|
||||
@@ -88,6 +88,16 @@ impl<'a> PublisherList<'a> {
|
||||
|
||||
//------------ PublisherDetails ----------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Rfc8181Details<'a> {
|
||||
#[serde(serialize_with = "ext_serde::ser_http_uri")]
|
||||
service_uri: uri::Http,
|
||||
|
||||
#[serde(serialize_with = "ext_serde::ser_id_cert")]
|
||||
id_cert: &'a IdCert
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct PublisherDetails<'a> {
|
||||
publisher_handle: &'a str,
|
||||
@@ -95,11 +105,7 @@ pub struct PublisherDetails<'a> {
|
||||
#[serde(serialize_with = "ext_serde::ser_rsync_uri")]
|
||||
base_uri: &'a uri::Rsync,
|
||||
|
||||
#[serde(serialize_with = "ext_serde::ser_http_uri")]
|
||||
service_uri: &'a uri::Http,
|
||||
|
||||
#[serde(serialize_with = "ext_serde::ser_id_cert")]
|
||||
identity_certificate: &'a IdCert,
|
||||
rfc8181: Option<Rfc8181Details<'a>>,
|
||||
|
||||
links: Vec<Link<'a>>
|
||||
}
|
||||
@@ -107,12 +113,25 @@ pub struct PublisherDetails<'a> {
|
||||
impl<'a> PublisherDetails<'a> {
|
||||
pub fn from(
|
||||
publisher: &'a Arc<Publisher>,
|
||||
path_publishers: &'a str
|
||||
path_publishers: &'a str,
|
||||
base_service_uri: &uri::Http
|
||||
) -> PublisherDetails<'a> {
|
||||
let handle = publisher.name().as_str();
|
||||
let handle = publisher.handle().as_str();
|
||||
let base_uri = publisher.base_uri();
|
||||
let service_uri = publisher.service_uri();
|
||||
let identity_certificate = publisher.id_cert();
|
||||
|
||||
// Derive the RFC8181 service URI.
|
||||
let service_uri = format!("{}{}", base_service_uri, handle);
|
||||
let service_uri = uri::Http::from_string(service_uri).unwrap();
|
||||
|
||||
let rfc8181 = match publisher.rfc8181() {
|
||||
None => None,
|
||||
Some(details) => Some(
|
||||
Rfc8181Details {
|
||||
service_uri,
|
||||
id_cert: details.id_cert()
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
let mut links = Vec::new();
|
||||
links.push(Link {
|
||||
@@ -123,8 +142,7 @@ impl<'a> PublisherDetails<'a> {
|
||||
PublisherDetails {
|
||||
publisher_handle: handle,
|
||||
base_uri,
|
||||
service_uri,
|
||||
identity_certificate,
|
||||
rfc8181,
|
||||
links
|
||||
}
|
||||
}
|
||||
|
||||
+14
-11
@@ -30,9 +30,6 @@ impl ConfigDefaults {
|
||||
fn rrdp_base_uri() -> uri::Http {
|
||||
uri::Http::from_str("http://127.0.0.1:3000/rrdp/").unwrap()
|
||||
}
|
||||
fn service_uri() -> uri::Http {
|
||||
uri::Http::from_str("http://127.0.0.1:3000/rfc8181/").unwrap()
|
||||
}
|
||||
fn log_level() -> LevelFilter { LevelFilter::Warn }
|
||||
fn log_type() -> LogType { LogType::Syslog }
|
||||
fn syslog_facility() -> Facility { Facility::LOG_DAEMON }
|
||||
@@ -86,12 +83,6 @@ pub struct Config {
|
||||
)]
|
||||
pub rrdp_base_uri: uri::Http,
|
||||
|
||||
#[serde(
|
||||
default = "ConfigDefaults::service_uri",
|
||||
deserialize_with = "ext_serde::de_http_uri"
|
||||
)]
|
||||
pub service_uri: uri::Http,
|
||||
|
||||
#[serde(
|
||||
default = "ConfigDefaults::log_level",
|
||||
deserialize_with = "ext_serde::de_level_filter"
|
||||
@@ -141,6 +132,20 @@ impl Config {
|
||||
path.push(ssl::KEY_FILE);
|
||||
path
|
||||
}
|
||||
|
||||
pub fn service_uri(&self) -> uri::Http {
|
||||
let mut uri = String::new();
|
||||
if self.use_ssl() {
|
||||
uri.push_str("https://");
|
||||
} else {
|
||||
uri.push_str("http://");
|
||||
}
|
||||
|
||||
uri.push_str(&self.socket_addr().to_string());
|
||||
uri.push_str("/");
|
||||
|
||||
uri::Http::from_string(uri).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// # Create
|
||||
@@ -154,7 +159,6 @@ impl Config {
|
||||
let data_dir = data_dir.clone();
|
||||
let rsync_base = ConfigDefaults::rsync_base();
|
||||
let rrdp_base_uri = ConfigDefaults::rrdp_base_uri();
|
||||
let service_uri = ConfigDefaults::service_uri();
|
||||
let log_level = ConfigDefaults::log_level();
|
||||
let log_type = LogType::Stderr;
|
||||
let log_file = ConfigDefaults::log_file();
|
||||
@@ -168,7 +172,6 @@ impl Config {
|
||||
data_dir,
|
||||
rsync_base,
|
||||
rrdp_base_uri,
|
||||
service_uri,
|
||||
log_level,
|
||||
log_type,
|
||||
log_file,
|
||||
|
||||
+66
-84
@@ -1,23 +1,29 @@
|
||||
//! Actix-web based HTTP server for the publication server.
|
||||
//!
|
||||
//! 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::error;
|
||||
use std::fs::File;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard};
|
||||
use actix_web::{pred, fs, server};
|
||||
use actix_web::{App, FromRequest, HttpResponse, ResponseError};
|
||||
use actix_web::{App, FromRequest, HttpResponse};
|
||||
use actix_web::dev::MessageBody;
|
||||
use actix_web::middleware;
|
||||
use actix_web::http::{Method, StatusCode };
|
||||
use bcder::decode;
|
||||
use futures::Future;
|
||||
use openssl::ssl::{SslMethod, SslAcceptor, SslAcceptorBuilder, SslFiletype};
|
||||
use crate::daemon::api::admin;
|
||||
use crate::daemon::api::endpoints;
|
||||
use crate::daemon::api::auth::{Authorizer, CheckAuthorisation};
|
||||
use crate::daemon::api::requests::PublishDelta;
|
||||
use crate::daemon::config::Config;
|
||||
use crate::daemon::http::ssl;
|
||||
use crate::daemon::krillserver;
|
||||
use crate::daemon::krillserver::KrillServer;
|
||||
use crate::remote::rfc8183::{PublisherRequest, PublisherRequestError};
|
||||
use crate::remote::rfc8183;
|
||||
use crate::remote::sigmsg::SignedMessage;
|
||||
use daemon::api::requests::PublisherRequestChoice;
|
||||
|
||||
const NOT_FOUND: &'static [u8] = include_bytes!("../../../ui/dev/html/404.html");
|
||||
|
||||
@@ -25,6 +31,7 @@ const NOT_FOUND: &'static [u8] = include_bytes!("../../../ui/dev/html/404.html")
|
||||
|
||||
pub struct PubServerApp(App<Arc<RwLock<KrillServer>>>);
|
||||
|
||||
|
||||
/// # Set up methods
|
||||
///
|
||||
impl PubServerApp {
|
||||
@@ -33,32 +40,32 @@ impl PubServerApp {
|
||||
.middleware(middleware::Logger::default())
|
||||
.middleware(CheckAuthorisation)
|
||||
.resource("/api/v1/publishers", |r| {
|
||||
r.method(Method::GET).f(admin::publishers);
|
||||
r.method(Method::POST).with(admin::add_publisher);
|
||||
r.method(Method::GET).f(endpoints::publishers);
|
||||
r.method(Method::POST).with(endpoints::add_publisher);
|
||||
})
|
||||
.resource("/api/v1/publishers/{handle}", |r| {
|
||||
r.method(Method::GET).with(admin::publisher_details);
|
||||
r.method(Method::POST).with(admin::add_named_publisher);
|
||||
r.method(Method::DELETE).with(admin::remove_publisher);
|
||||
r.method(Method::GET).with(endpoints::publisher_details);
|
||||
r.method(Method::POST).with(endpoints::add_named_publisher);
|
||||
r.method(Method::DELETE).with(endpoints::remove_publisher);
|
||||
})
|
||||
// For clients that cannot handle http methods
|
||||
.resource("/api/v1/publishers/{handle}/del", |r| {
|
||||
r.method(Method::POST).with(admin::remove_publisher);
|
||||
r.method(Method::POST).with(endpoints::remove_publisher);
|
||||
})
|
||||
.resource("/api/v1/publishers/{handle}/response.xml", |r| {
|
||||
r.method(Method::GET).with(admin::repository_response)
|
||||
})
|
||||
.resource("/api/v1/health", |r| {
|
||||
r.method(Method::GET).f(Self::api_ok)
|
||||
r.method(Method::GET).with(endpoints::repository_response)
|
||||
})
|
||||
.resource("/rfc8181/{handle}", |r| {
|
||||
r.method(Method::POST).with(Self::process_publish_request)
|
||||
r.method(Method::POST).with(endpoints::handle_rfc8181_request)
|
||||
})
|
||||
.resource("/rrdp/{path:.*}", |r| {
|
||||
r.method(Method::GET).f(Self::serve_rrdp_files)
|
||||
})
|
||||
.resource("/health", |r| {
|
||||
r.method(Method::GET).f(Self::service_ok)
|
||||
.resource("/health", |r| { // No authentication required
|
||||
r.method(Method::GET).f(endpoints::health)
|
||||
})
|
||||
.resource("/api/v1/health", |r| { // health with authentication
|
||||
r.method(Method::GET).f(endpoints::health)
|
||||
})
|
||||
.default_resource(|r| {
|
||||
// 404 for GET request
|
||||
@@ -90,7 +97,7 @@ impl PubServerApp {
|
||||
let pub_server = match KrillServer::new(
|
||||
&config.data_dir,
|
||||
&config.rsync_base,
|
||||
&config.service_uri,
|
||||
config.service_uri(),
|
||||
&config.rrdp_base_uri,
|
||||
authorizer
|
||||
) {
|
||||
@@ -103,7 +110,7 @@ impl PubServerApp {
|
||||
Arc::new(RwLock::new(pub_server))
|
||||
}
|
||||
|
||||
/// Used to start the server with an existing executor (e.g. in tests)
|
||||
/// Used to start the server with an existing executor (for tests)
|
||||
///
|
||||
/// Note https is not supported in tests.
|
||||
pub fn start(config: &Config) {
|
||||
@@ -144,6 +151,8 @@ impl PubServerApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
if config.test_ssl() {
|
||||
ssl::create_key_cert_if_needed(&config.data_dir)
|
||||
@@ -180,45 +189,6 @@ impl PubServerApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes an RFC8181 query and returns the appropriate response.
|
||||
///
|
||||
/// Note this method checks whether the request can be decoded only, and
|
||||
/// if successful delegates to [`handle_signed_request`] for further
|
||||
/// processing.
|
||||
///
|
||||
/// [`handle_signed_request`]: #method.handle_signed_request
|
||||
fn process_publish_request(
|
||||
req: HttpRequest,
|
||||
handle: PublisherHandle,
|
||||
msg: SignedMessage
|
||||
) -> HttpResponse {
|
||||
debug!("Processing publish request");
|
||||
Self::handle_signed_request(
|
||||
req.state().write().unwrap(),
|
||||
&msg,
|
||||
handle.0.as_str()
|
||||
)
|
||||
}
|
||||
|
||||
/// Handles a decoded RFC8181 query.
|
||||
///
|
||||
/// This delegates to `PubServer` to do the actual hard work.
|
||||
fn handle_signed_request(
|
||||
mut server: RwLockWriteGuard<KrillServer>,
|
||||
msg: &SignedMessage,
|
||||
handle: &str
|
||||
) -> HttpResponse {
|
||||
match server.handle_request(msg, handle) {
|
||||
Ok(captured) => {
|
||||
HttpResponse::build(StatusCode::OK)
|
||||
.content_type("application/rpki-publication")
|
||||
.body(captured.into_bytes())
|
||||
}
|
||||
Err(e) => {
|
||||
Self::server_error(Error::ServerError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XXX TODO: use a better handler that does not load everything into
|
||||
// memory first, and set the correct headers for caching.
|
||||
@@ -248,24 +218,6 @@ impl PubServerApp {
|
||||
None => Self::p404(req)
|
||||
}
|
||||
}
|
||||
|
||||
/// API health check, expect that caller authenticates.
|
||||
fn api_ok(_r: &HttpRequest) -> HttpResponse {
|
||||
HttpResponse::Ok().body("")
|
||||
}
|
||||
|
||||
/// Simple human server status response page.
|
||||
fn service_ok(_r: &HttpRequest) -> HttpResponse {
|
||||
// XXX TODO: do a real check
|
||||
HttpResponse::Ok().body("I am completely operational, and all my circuits are functioning perfectly.")
|
||||
}
|
||||
|
||||
/// Helper function to render server side errors. Also responsible for
|
||||
/// logging the errors.
|
||||
fn server_error(error: Error) -> HttpResponse {
|
||||
error!("{}", error);
|
||||
error.error_response()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,10 +265,14 @@ impl Default for SignedMessageConvertConfig {
|
||||
}
|
||||
}
|
||||
|
||||
//------------ PublisherRequest ----------------------------------------------
|
||||
|
||||
/// Support converting requests into PublisherRequest
|
||||
impl<S: 'static> FromRequest<S> for PublisherRequest {
|
||||
//------------ PublisherRequestChoice ----------------------------------------
|
||||
|
||||
/// Converts the body sent to 'add publisher' end-points to a
|
||||
/// PublisherRequestChoice, which contains either an
|
||||
/// rfc8183::PublisherRequest, or an API publisher request (no ID certs and
|
||||
/// CMS etc).
|
||||
impl<S: 'static> FromRequest<S> for PublisherRequestChoice {
|
||||
type Config = ();
|
||||
type Result = Box<Future<Item=Self, Error=actix_web::Error>>;
|
||||
|
||||
@@ -327,12 +283,15 @@ impl<S: 'static> FromRequest<S> for PublisherRequest {
|
||||
Box::new(MessageBody::new(req)
|
||||
.from_err()
|
||||
.and_then(|bytes| {
|
||||
match PublisherRequest::decode(bytes.as_ref()) {
|
||||
Ok(req) => Ok(req),
|
||||
Err(e) => Err(Error::PublisherRequestError(e).into())
|
||||
if bytes.starts_with(b"<") { // check content-type instead?
|
||||
match rfc8183::PublisherRequest::decode(bytes.as_ref()) {
|
||||
Ok(req) => Ok(PublisherRequestChoice::Rfc8183(req)),
|
||||
Err(e) => Err(Error::Wrong8183Xml(e).into())
|
||||
}
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
})
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -368,6 +327,29 @@ impl AsRef<str> for PublisherHandle {
|
||||
}
|
||||
|
||||
|
||||
//------------ PublishDelta --------------------------------------------------
|
||||
/// Support converting request body into PublishDelta
|
||||
impl<S: 'static> FromRequest<S> for PublishDelta {
|
||||
type Config = ();
|
||||
type Result = Box<Future<Item=Self, Error=actix_web::Error>>;
|
||||
|
||||
fn from_request(
|
||||
req: &actix_web::HttpRequest<S>,
|
||||
_cfg: &Self::Config
|
||||
) -> Self::Result {
|
||||
Box::new(MessageBody::new(req)
|
||||
.from_err()
|
||||
.and_then(|bytes| {
|
||||
let delta: PublishDelta =
|
||||
serde_json::from_reader(bytes.as_ref())
|
||||
.map_err(|e| Error::JsonError(e))?;
|
||||
Ok(delta)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------ IntoHttpHandler -----------------------------------------------
|
||||
|
||||
impl server::IntoHttpHandler for PubServerApp {
|
||||
@@ -409,7 +391,7 @@ pub enum Error {
|
||||
DecodeError(decode::Error),
|
||||
|
||||
#[display(fmt = "Cannot decode request: {}", _0)]
|
||||
PublisherRequestError(PublisherRequestError),
|
||||
Wrong8183Xml(rfc8183::PublisherRequestError),
|
||||
|
||||
#[display(fmt = "Wrong path")]
|
||||
WrongPath,
|
||||
|
||||
+39
-17
@@ -10,8 +10,9 @@ use crate::daemon::api::responses;
|
||||
use crate::daemon::publishers::{self, Publisher, PublisherStore};
|
||||
use crate::daemon::repo::{self, Repository, RRDP_FOLDER};
|
||||
use crate::remote::cmsproxy::{self, CmsProxy};
|
||||
use crate::remote::rfc8183::{PublisherRequest, RepositoryResponse};
|
||||
use crate::remote::sigmsg::SignedMessage;
|
||||
use daemon::api::requests::PublisherRequestChoice;
|
||||
use remote::rfc8183;
|
||||
|
||||
/// # Naming things in the keystore.
|
||||
const ACTOR: &'static str = "krill pubd";
|
||||
@@ -33,6 +34,9 @@ const ACTOR: &'static str = "krill pubd";
|
||||
/// * Updates the RRDP files
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KrillServer {
|
||||
// The base URI for this service
|
||||
service_uri: uri::Http,
|
||||
|
||||
// The base working directory, used for various storage
|
||||
work_dir: PathBuf,
|
||||
|
||||
@@ -56,16 +60,17 @@ impl KrillServer {
|
||||
pub fn new(
|
||||
work_dir: &PathBuf,
|
||||
base_uri: &uri::Rsync,
|
||||
service_uri: &uri::Http,
|
||||
service_uri: uri::Http,
|
||||
rrdp_base_uri: &uri::Http,
|
||||
authorizer: Authorizer
|
||||
) -> Result<Self, Error> {
|
||||
let cms_proxy = CmsProxy::new(work_dir, service_uri)?;
|
||||
let cms_proxy = CmsProxy::new(work_dir)?;
|
||||
let publisher_store = PublisherStore::new(work_dir, base_uri)?;
|
||||
let repository = Repository::new(rrdp_base_uri, work_dir)?;
|
||||
|
||||
Ok(
|
||||
KrillServer {
|
||||
service_uri,
|
||||
work_dir: work_dir.clone(),
|
||||
authorizer,
|
||||
cms_proxy,
|
||||
@@ -74,6 +79,10 @@ impl KrillServer {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn service_base_uri(&self) -> &uri::Http {
|
||||
&self.service_uri
|
||||
}
|
||||
}
|
||||
|
||||
impl KrillServer {
|
||||
@@ -94,13 +103,12 @@ impl KrillServer {
|
||||
/// Adds the publishers, blows up if it already existed.
|
||||
pub fn add_publisher(
|
||||
&mut self,
|
||||
req: PublisherRequest,
|
||||
handle: &str,
|
||||
prc: PublisherRequestChoice,
|
||||
handle_override: Option<&str>,
|
||||
) -> Result<(), Error> {
|
||||
self.publisher_store.add_publisher(
|
||||
req,
|
||||
handle,
|
||||
self.cms_proxy.base_service_uri(),
|
||||
prc,
|
||||
handle_override,
|
||||
ACTOR
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -133,11 +141,14 @@ impl KrillServer {
|
||||
pub fn repository_response(
|
||||
&self,
|
||||
name: impl AsRef<str>
|
||||
) -> Result<RepositoryResponse, Error> {
|
||||
) -> Result<rfc8183::RepositoryResponse, Error> {
|
||||
let publisher = self.publisher_store.get_publisher(name)?;
|
||||
let rrdp_notification = self.repository.rrdp_notification_uri();
|
||||
self.cms_proxy
|
||||
.repository_response(publisher, rrdp_notification)
|
||||
.repository_response(
|
||||
publisher,
|
||||
self.service_base_uri(),
|
||||
rrdp_notification)
|
||||
.map_err(|e| Error::CmsProxy(e))
|
||||
}
|
||||
|
||||
@@ -164,7 +175,7 @@ impl KrillServer {
|
||||
/// 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(
|
||||
pub fn handle_rfc8181_request(
|
||||
&mut self,
|
||||
sigmsg: &SignedMessage,
|
||||
handle: &str
|
||||
@@ -172,15 +183,23 @@ impl KrillServer {
|
||||
debug!("Handling request for: {}", handle);
|
||||
let publisher = self.publisher_store.get_publisher(handle)?;
|
||||
|
||||
match self.cms_proxy.publish_request(sigmsg, publisher.id_cert()) {
|
||||
let id_cert = match publisher.rfc8181() {
|
||||
Some(details) => details.id_cert(),
|
||||
None => return Err(Error::NoIdCert)
|
||||
};
|
||||
|
||||
match self.cms_proxy.publish_request(sigmsg, id_cert) {
|
||||
Err(e) => self.cms_proxy.wrap_error(e).map_err(|e| Error::CmsProxy(e)),
|
||||
Ok(req) => {
|
||||
let reply = match req {
|
||||
PublishRequest::List => {
|
||||
self.handle_list(handle)
|
||||
self.handle_list(handle).map(|list|
|
||||
responses::PublishReply::List(list))
|
||||
|
||||
},
|
||||
PublishRequest::Delta(delta) => {
|
||||
self.handle_delta(delta, handle)
|
||||
self.handle_delta(delta, handle).map(|_|
|
||||
responses::PublishReply::Success)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +218,7 @@ impl KrillServer {
|
||||
&mut self,
|
||||
delta: PublishDelta,
|
||||
handle: &str
|
||||
) -> Result<responses::PublishReply, Error> {
|
||||
) -> Result<(), Error> {
|
||||
let publisher = self.publisher_store.get_publisher(handle)?;
|
||||
let base_uri = publisher.base_uri();
|
||||
self.repository.publish(&delta, base_uri)
|
||||
@@ -208,9 +227,9 @@ impl KrillServer {
|
||||
|
||||
/// Handles a list request sent to the API, or.. through the CmsProxy.
|
||||
pub fn handle_list(
|
||||
&mut self,
|
||||
&self,
|
||||
handle: &str
|
||||
) -> Result<responses::PublishReply, Error> {
|
||||
) -> Result<responses::ListReply, Error> {
|
||||
let publisher = self.publisher_store.get_publisher(handle)?;
|
||||
let base_uri = publisher.base_uri();
|
||||
self.repository.list(base_uri).map_err(|e| Error::Repository(e))
|
||||
@@ -231,6 +250,9 @@ pub enum Error {
|
||||
|
||||
#[display(fmt="{}", _0)]
|
||||
Repository(repo::Error),
|
||||
|
||||
#[display(fmt="No IdCert known for this publisher")]
|
||||
NoIdCert
|
||||
}
|
||||
|
||||
impl From<cmsproxy::Error> for Error {
|
||||
|
||||
+177
-197
@@ -5,91 +5,29 @@ use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use rpki::uri;
|
||||
use daemon::api::requests;
|
||||
use daemon::api::requests::PublisherRequestChoice;
|
||||
use crate::remote::id::IdCert;
|
||||
use crate::remote::rfc8183::{PublisherRequest, PublisherRequestError};
|
||||
use crate::remote::rfc8183;
|
||||
use crate::storage::keystore::{self, Info, Key, KeyStore};
|
||||
use crate::storage::caching_ks::CachingDiskKeyStore;
|
||||
use crate::util::ext_serde;
|
||||
|
||||
//------------ Publisher -----------------------------------------------------
|
||||
|
||||
/// This type defines Publisher CAs that are allowed to publish.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Publisher {
|
||||
pub struct Rfc8181PublisherDetails {
|
||||
// The optional tag in the request. None maps to empty string.
|
||||
tag: String,
|
||||
|
||||
name: String,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_rsync_uri",
|
||||
serialize_with = "ext_serde::ser_rsync_uri")]
|
||||
base_uri: uri::Rsync,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_http_uri",
|
||||
serialize_with = "ext_serde::ser_http_uri")]
|
||||
service_uri: uri::Http,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_id_cert",
|
||||
serialize_with = "ext_serde::ser_id_cert")]
|
||||
id_cert: IdCert
|
||||
}
|
||||
|
||||
impl Publisher {
|
||||
pub fn new(
|
||||
tag: Option<String>,
|
||||
name: String,
|
||||
base_uri: uri::Rsync,
|
||||
service_uri: uri::Http,
|
||||
id_cert: IdCert
|
||||
) -> Self {
|
||||
|
||||
let tag = tag.unwrap_or("".to_string());
|
||||
|
||||
Publisher {
|
||||
tag,
|
||||
name,
|
||||
base_uri,
|
||||
service_uri,
|
||||
id_cert
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new Publisher that is the same as this Publisher, except
|
||||
/// that it has an updated IdCert
|
||||
pub fn with_new_id_cert(&self, id_cert: IdCert) -> Self {
|
||||
Publisher {
|
||||
tag: self.tag.clone(),
|
||||
name: self.name.clone(),
|
||||
base_uri: self.base_uri.clone(),
|
||||
service_uri: self.service_uri.clone(),
|
||||
id_cert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Publisher {
|
||||
pub fn tag(&self) -> Option<String> {
|
||||
let tag = &self.tag;
|
||||
if tag.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(tag.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn base_uri(&self) -> &uri::Rsync {
|
||||
&self.base_uri
|
||||
}
|
||||
|
||||
pub fn service_uri(&self) -> &uri::Http {
|
||||
&self.service_uri
|
||||
impl Rfc8181PublisherDetails {
|
||||
pub fn tag(&self) -> &String {
|
||||
&self.tag
|
||||
}
|
||||
|
||||
pub fn id_cert(&self) -> &IdCert {
|
||||
@@ -97,12 +35,80 @@ impl Publisher {
|
||||
}
|
||||
}
|
||||
|
||||
impl Rfc8181PublisherDetails {
|
||||
pub fn new(tag: Option<String>, id_cert: IdCert) -> Self {
|
||||
let tag = tag.unwrap_or("".to_string());
|
||||
Rfc8181PublisherDetails { tag, id_cert }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Rfc8181PublisherDetails {
|
||||
fn eq(&self, other: &Rfc8181PublisherDetails) -> bool {
|
||||
self.tag == other.tag &&
|
||||
self.id_cert.to_bytes() == other.id_cert.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Rfc8181PublisherDetails {}
|
||||
|
||||
|
||||
//------------ Publisher -----------------------------------------------------
|
||||
|
||||
/// This type defines Publisher CAs that are allowed to publish.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Publisher {
|
||||
handle: String,
|
||||
|
||||
/// The token used by the API
|
||||
token: String,
|
||||
|
||||
#[serde(
|
||||
deserialize_with = "ext_serde::de_rsync_uri",
|
||||
serialize_with = "ext_serde::ser_rsync_uri")]
|
||||
base_uri: uri::Rsync,
|
||||
|
||||
rfc8181: Option<Rfc8181PublisherDetails>
|
||||
}
|
||||
|
||||
impl Publisher {
|
||||
pub fn new(
|
||||
handle: String,
|
||||
token: String,
|
||||
base_uri: uri::Rsync,
|
||||
rfc8181: Option<Rfc8181PublisherDetails>
|
||||
) -> Self {
|
||||
Publisher {
|
||||
handle,
|
||||
token,
|
||||
base_uri,
|
||||
rfc8181
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Publisher {
|
||||
pub fn handle(&self) -> &String {
|
||||
&self.handle
|
||||
}
|
||||
|
||||
pub fn token(&self) -> &String {
|
||||
&self.token
|
||||
}
|
||||
|
||||
pub fn base_uri(&self) -> &uri::Rsync {
|
||||
&self.base_uri
|
||||
}
|
||||
|
||||
pub fn rfc8181(&self) -> &Option<Rfc8181PublisherDetails> {
|
||||
&self.rfc8181
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Publisher {
|
||||
fn eq(&self, other: &Publisher) -> bool {
|
||||
self.name == other.name &&
|
||||
self.base_uri == other.base_uri &&
|
||||
self.service_uri == other.service_uri &&
|
||||
self.id_cert.to_bytes() == other.id_cert.to_bytes()
|
||||
self.handle == other.handle &&
|
||||
self.base_uri == other.base_uri &&
|
||||
self.rfc8181 == other.rfc8181
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,19 +147,7 @@ impl PublisherStore {
|
||||
)
|
||||
}
|
||||
|
||||
/// Adds a Publisher based on a PublisherRequest (from the RFC 8183 xml).
|
||||
///
|
||||
/// Will return an error if the publisher already exists! Use
|
||||
/// update_publisher in case you want to update an existing publisher.
|
||||
pub fn add_publisher(
|
||||
&mut self,
|
||||
pr: PublisherRequest,
|
||||
handle: &str,
|
||||
base_service_uri: &uri::Http,
|
||||
actor: &str
|
||||
) -> Result<(), Error> {
|
||||
let (tag, _, id_cert) = pr.into_parts();
|
||||
|
||||
fn verify_handle(&self, handle: &str) -> Result<(), Error> {
|
||||
if handle.contains("/") {
|
||||
return Err(Error::ForwardSlashInHandle(handle.to_string()))
|
||||
}
|
||||
@@ -162,29 +156,74 @@ impl PublisherStore {
|
||||
return Err(Error::DuplicatePublisher(handle.to_string()))
|
||||
}
|
||||
|
||||
let mut base_uri = self.base_uri.to_string();
|
||||
base_uri.push_str(handle);
|
||||
base_uri.push_str("/");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn publisher_base_uri(&self, handle: &str) -> Result<uri::Rsync, Error> {
|
||||
let base_uri = format!("{}{}/", self.base_uri.to_string(), handle);
|
||||
let base_uri = uri::Rsync::from_string(base_uri)?;
|
||||
Ok(base_uri)
|
||||
}
|
||||
|
||||
let mut service_uri = base_service_uri.to_string();
|
||||
service_uri.push_str(handle);
|
||||
let service_uri = uri::Http::from_string(service_uri)?;
|
||||
/// Adds a Publisher based on a PublisherRequest (from the RFC 8183 xml).
|
||||
///
|
||||
/// Will return an error if the publisher already exists! Use
|
||||
/// update_publisher in case you want to update an existing publisher.
|
||||
pub fn add_publisher(
|
||||
&mut self,
|
||||
prc: PublisherRequestChoice,
|
||||
handle_override: Option<&str>,
|
||||
actor: &str
|
||||
) -> Result<(), Error> {
|
||||
|
||||
let key = Key::from_str(handle);
|
||||
let publisher = match prc {
|
||||
PublisherRequestChoice::Api(pr) => {
|
||||
if handle_override.is_some() {
|
||||
return Err(Error::HandleOverrideNotAllowed)
|
||||
}
|
||||
let (handle, token) = pr.parts();
|
||||
self.verify_handle(&handle)?;
|
||||
let base_uri = self.publisher_base_uri(&handle)?;
|
||||
|
||||
Publisher::new(
|
||||
handle,
|
||||
token,
|
||||
base_uri,
|
||||
None
|
||||
)
|
||||
},
|
||||
PublisherRequestChoice::Rfc8183(pr) => {
|
||||
let (tag, mut handle, id_cert) = pr.into_parts();
|
||||
|
||||
match handle_override {
|
||||
Some(handle_override) => {
|
||||
handle = handle_override.to_string()
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.verify_handle(&handle)?;
|
||||
let base_uri = self.publisher_base_uri(&handle)?;
|
||||
let rfc8181 = Rfc8181PublisherDetails::new(tag, id_cert);
|
||||
|
||||
let token = requests::generate_random_token();
|
||||
|
||||
Publisher::new(
|
||||
handle,
|
||||
token,
|
||||
base_uri,
|
||||
Some(rfc8181)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let key = Key::from_str(&publisher.handle);
|
||||
let info = Info::now(
|
||||
actor,
|
||||
&format!("Added publisher: {}", handle)
|
||||
);
|
||||
let publisher = Publisher::new(
|
||||
tag,
|
||||
handle.to_string(),
|
||||
base_uri,
|
||||
service_uri,
|
||||
id_cert
|
||||
&format!("Added publisher: {}", &publisher.handle)
|
||||
);
|
||||
|
||||
info!("Adding publisher: {}", handle);
|
||||
info!("Added publisher: {}", &publisher.handle);
|
||||
self.store.store(key, publisher, info)?;
|
||||
|
||||
Ok(())
|
||||
@@ -195,10 +234,10 @@ impl PublisherStore {
|
||||
/// Will return an error if ths publisher does not exist.
|
||||
pub fn remove_publisher(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
handle: impl AsRef<str>,
|
||||
actor: &str
|
||||
) -> Result<(), Error> {
|
||||
let name = name.as_ref();
|
||||
let name = handle.as_ref();
|
||||
match self.publisher(name)? {
|
||||
None => Err(Error::UnknownPublisher(name.to_string())),
|
||||
Some(_p) => {
|
||||
@@ -215,43 +254,15 @@ impl PublisherStore {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Updates the IdCert for a known publisher.
|
||||
pub fn update_id_cert_publisher(
|
||||
&mut self,
|
||||
name: &str,
|
||||
id_cert: IdCert,
|
||||
actor: &str
|
||||
) -> Result<(), Error> {
|
||||
let publisher_opt = self.publisher(name)?;
|
||||
|
||||
match publisher_opt {
|
||||
None => Err(Error::UnknownPublisher(name.to_string())),
|
||||
Some(publisher) => {
|
||||
let key = Key::from_str(name);
|
||||
let new_publisher = publisher.with_new_id_cert(id_cert);
|
||||
let info = Info::now(
|
||||
actor,
|
||||
"Updated the IdCert"
|
||||
);
|
||||
|
||||
info!("Updated Id for publisher: {}", publisher.name());
|
||||
self.store.store(key, new_publisher, info)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether a publisher exists for this name.
|
||||
pub fn has_publisher(&self, name: &str) -> bool {
|
||||
let key = Key::from_str(name);
|
||||
pub fn has_publisher(&self, handle: &str) -> bool {
|
||||
let key = Key::from_str(handle);
|
||||
match self.store.version(&key) {
|
||||
Ok(Some(version)) => {
|
||||
if version > 0 {
|
||||
true
|
||||
} else {
|
||||
debug!("Publisher {} was archived.", name);
|
||||
debug!("Publisher {} was archived.", handle);
|
||||
false
|
||||
}
|
||||
},
|
||||
@@ -262,9 +273,9 @@ impl PublisherStore {
|
||||
/// Returns an Optional Arc to a publisher for this name.
|
||||
pub fn publisher(
|
||||
&self,
|
||||
name: impl AsRef<str>
|
||||
handle: impl AsRef<str>
|
||||
) -> Result<Option<Arc<Publisher>>, Error> {
|
||||
let key = Key::from_str(name.as_ref());
|
||||
let key = Key::from_str(handle.as_ref());
|
||||
self.store.get(&key).map_err(|e| { Error::KeyStoreError(e)})
|
||||
}
|
||||
|
||||
@@ -272,9 +283,9 @@ impl PublisherStore {
|
||||
/// does not exist.
|
||||
pub fn get_publisher(
|
||||
&self,
|
||||
name: impl AsRef<str>
|
||||
handle: impl AsRef<str>
|
||||
) -> Result<Arc<Publisher>, Error> {
|
||||
let name = name.as_ref();
|
||||
let name = handle.as_ref();
|
||||
match self.publisher(name)? {
|
||||
None => Err(Error::UnknownPublisher(name.to_string())),
|
||||
Some(p) => Ok(p)
|
||||
@@ -324,8 +335,11 @@ pub enum Error {
|
||||
#[display(fmt = "Error in base URI: {}.", _0)]
|
||||
UriError(uri::Error),
|
||||
|
||||
#[display(fmt = "Invalide Publisher Request: {}.", _0)]
|
||||
PublisherRequestError(PublisherRequestError)
|
||||
#[display(fmt = "Invalid Publisher Request: {}.", _0)]
|
||||
PublisherRequestError(rfc8183::PublisherRequestError),
|
||||
|
||||
#[display(fmt = "Cannot override handle using path parameter for json api")]
|
||||
HandleOverrideNotAllowed
|
||||
}
|
||||
|
||||
impl From<keystore::Error> for Error {
|
||||
@@ -346,8 +360,8 @@ impl From<uri::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublisherRequestError> for Error {
|
||||
fn from(e: PublisherRequestError) -> Self {
|
||||
impl From<rfc8183::PublisherRequestError> for Error {
|
||||
fn from(e: rfc8183::PublisherRequestError) -> Self {
|
||||
Error::PublisherRequestError(e)
|
||||
}
|
||||
}
|
||||
@@ -361,10 +375,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::util::test;
|
||||
|
||||
fn base_service_uri() -> uri::Http {
|
||||
test::http_uri("http://127.0.0.1:3000/rfc8181/")
|
||||
}
|
||||
|
||||
fn test_publisher_store(dir: &PathBuf) -> PublisherStore {
|
||||
let uri = test::rsync_uri("rsync://host/module/");
|
||||
PublisherStore::new(dir, &uri).unwrap()
|
||||
@@ -375,10 +385,11 @@ mod tests {
|
||||
test::test_with_tmp_dir(|d| {
|
||||
let mut ps = test_publisher_store(&d);
|
||||
let pr = test::new_publisher_request("test/below", &d);
|
||||
let prc = PublisherRequestChoice::Rfc8183(pr);
|
||||
|
||||
let handle = "test/below";
|
||||
|
||||
match ps.add_publisher(pr, handle, &base_service_uri(), "test") {
|
||||
match ps.add_publisher(prc, Some(handle), "test") {
|
||||
Err(Error::ForwardSlashInHandle(_)) => { }, // Ok
|
||||
_ => panic!("Should have seen error.")
|
||||
}
|
||||
@@ -390,27 +401,29 @@ mod tests {
|
||||
test::test_with_tmp_dir(|d| {
|
||||
let mut ps = test_publisher_store(&d);
|
||||
let name = "alice";
|
||||
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
let id_cert = pr.id_cert().clone();
|
||||
|
||||
let prc = PublisherRequestChoice::Rfc8183(pr);
|
||||
|
||||
let actor = "test";
|
||||
|
||||
ps.add_publisher(pr, name, &base_service_uri(), actor).unwrap();
|
||||
ps.add_publisher(prc, Some(name), actor).unwrap();
|
||||
|
||||
assert!(ps.has_publisher(&name));
|
||||
|
||||
// Get the Arc out of the Result<Option<Arc<Publisher>>, Error>
|
||||
let publisher_found = ps.publisher(&name).unwrap().unwrap();
|
||||
|
||||
let expected_publisher = Publisher::new(
|
||||
None,
|
||||
name.to_string(),
|
||||
test::rsync_uri(&format!("rsync://host/module/{}/", name)),
|
||||
test::http_uri(
|
||||
&format!("http://127.0.0.1:3000/rfc8181/{}",name)),
|
||||
id_cert
|
||||
);
|
||||
let expected_rfc8181 = Rfc8181PublisherDetails::new(None, id_cert);
|
||||
|
||||
assert_eq!(publisher_found.handle(), "alice");
|
||||
assert_eq!(
|
||||
publisher_found.base_uri().to_string().as_str(),
|
||||
"rsync://host/module/alice/");
|
||||
assert_eq!(publisher_found.rfc8181(), &Some(expected_rfc8181));
|
||||
|
||||
assert_eq!(publisher_found.as_ref(), &expected_publisher);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -420,13 +433,15 @@ mod tests {
|
||||
let mut ps = test_publisher_store(&d);
|
||||
let name = "alice";
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
let prc = PublisherRequestChoice::Rfc8183(pr);
|
||||
let actor = "test";
|
||||
|
||||
ps.add_publisher(pr, name, &base_service_uri(), actor).unwrap();
|
||||
ps.add_publisher(prc, None, actor).unwrap();
|
||||
assert!(ps.has_publisher(&name));
|
||||
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
match ps.add_publisher(pr, name, &base_service_uri(), actor) {
|
||||
let prc = PublisherRequestChoice::Rfc8183(pr);
|
||||
match ps.add_publisher(prc, None, actor) {
|
||||
Err(Error::DuplicatePublisher(_)) => { }, // Ok
|
||||
_ => panic!("Should have seen error.")
|
||||
}
|
||||
@@ -434,42 +449,6 @@ mod tests {
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_update_id_cert_publisher() {
|
||||
test::test_with_tmp_dir(|d| {
|
||||
let mut ps = test_publisher_store(&d);
|
||||
let name = "alice";
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
let actor = "test";
|
||||
|
||||
ps.add_publisher(pr, name, &base_service_uri(), actor).unwrap();
|
||||
|
||||
// Make a new publisher request for alice, using a new cert
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
let id_cert = pr.id_cert().clone();
|
||||
|
||||
ps.update_id_cert_publisher(
|
||||
name,
|
||||
id_cert.clone(),
|
||||
actor.clone()
|
||||
).unwrap();
|
||||
|
||||
// Get the Arc out of the Result<Option<Arc<Publisher>>, Error>
|
||||
let publisher_found = ps.publisher(&name).unwrap().unwrap();
|
||||
|
||||
let expected_publisher = Publisher::new(
|
||||
None,
|
||||
name.to_string(),
|
||||
test::rsync_uri(&format!("rsync://host/module/{}/", name)),
|
||||
test::http_uri(
|
||||
&format!("http://127.0.0.1:3000/rfc8181/{}",name)),
|
||||
id_cert
|
||||
);
|
||||
|
||||
assert_eq!(publisher_found.as_ref(), &expected_publisher);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_remove_publisher() {
|
||||
test::test_with_tmp_dir(|d| {
|
||||
@@ -478,8 +457,9 @@ mod tests {
|
||||
let name = "alice";
|
||||
let actor = "test";
|
||||
let pr = test::new_publisher_request(name, &d);
|
||||
let prc = PublisherRequestChoice::Rfc8183(pr);
|
||||
|
||||
ps.add_publisher(pr, name, &base_service_uri(), actor).unwrap();
|
||||
ps.add_publisher(prc, None, actor).unwrap();
|
||||
assert_eq!(1, ps.publishers().unwrap(). len());
|
||||
|
||||
ps.remove_publisher(name, actor).unwrap();
|
||||
|
||||
+4
-5
@@ -63,11 +63,11 @@ impl Repository {
|
||||
&mut self,
|
||||
delta: &PublishDelta,
|
||||
base_uri: &uri::Rsync
|
||||
) -> Result<responses::PublishReply, Error> {
|
||||
) -> Result<(), Error> {
|
||||
debug!("Processing update with {} elements", delta.len());
|
||||
self.fs.publish(delta, base_uri)?;
|
||||
self.rrdp.publish(delta)?;
|
||||
Ok(responses::PublishReply::Success)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lists the objects for a base_uri, presumably all for the same
|
||||
@@ -75,11 +75,10 @@ impl Repository {
|
||||
pub fn list(
|
||||
&self,
|
||||
base_uri: &uri::Rsync
|
||||
) -> Result<responses::PublishReply, Error> {
|
||||
) -> Result<responses::ListReply, Error> {
|
||||
debug!("Processing list query");
|
||||
let files = self.fs.list(base_uri)?;
|
||||
Ok(responses::PublishReply::List(
|
||||
responses::ListReply::new(files)))
|
||||
Ok(responses::ListReply::new(files))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ extern crate serde_json;
|
||||
extern crate syslog;
|
||||
extern crate tokio;
|
||||
extern crate toml;
|
||||
extern crate uuid;
|
||||
extern crate xml as xmlrs;
|
||||
|
||||
// XXX Temporarily
|
||||
|
||||
+15
-11
@@ -29,23 +29,15 @@ use crate::remote::sigmsg::SignedMessage;
|
||||
pub struct CmsProxy {
|
||||
// The component that manages server id, and wraps responses to clients
|
||||
responder: Responder,
|
||||
|
||||
// The URI that publishers need to access to publish (see config)
|
||||
service_uri: uri::Http,
|
||||
}
|
||||
|
||||
/// # Set up
|
||||
impl CmsProxy {
|
||||
pub fn new(
|
||||
work_dir: &PathBuf,
|
||||
service_uri: &uri::Http
|
||||
work_dir: &PathBuf
|
||||
) -> Result<Self, Error> {
|
||||
let responder = Responder::init(work_dir)?;
|
||||
Ok(CmsProxy { responder, service_uri: service_uri.clone() })
|
||||
}
|
||||
|
||||
pub fn base_service_uri(&self) -> &uri::Http {
|
||||
&self.service_uri
|
||||
Ok(CmsProxy { responder })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +99,22 @@ impl CmsProxy {
|
||||
pub fn repository_response(
|
||||
&self,
|
||||
publisher: Arc<Publisher>,
|
||||
base_service_uri: &uri::Http,
|
||||
rrdp_notification_uri: uri::Http
|
||||
) -> Result<rfc8183::RepositoryResponse, Error> {
|
||||
|
||||
let service_uri = format!(
|
||||
"{}rfc8181/{}",
|
||||
base_service_uri.to_string(),
|
||||
publisher.handle()
|
||||
);
|
||||
let service_uri = uri::Http::from_string(service_uri).unwrap();
|
||||
|
||||
self.responder
|
||||
.repository_response(publisher, rrdp_notification_uri)
|
||||
.repository_response(
|
||||
publisher,
|
||||
service_uri,
|
||||
rrdp_notification_uri)
|
||||
.map_err(|e| Error::ResponderError(e))
|
||||
}
|
||||
|
||||
|
||||
+24
-9
@@ -96,20 +96,26 @@ impl Responder {
|
||||
pub fn repository_response(
|
||||
&self,
|
||||
publisher: Arc<Publisher>,
|
||||
service_uri: uri::Http,
|
||||
rrdp_notification_uri: uri::Http
|
||||
) -> Result<RepositoryResponse, Error> {
|
||||
|
||||
if let Some(my_id) = self.my_identity()? {
|
||||
let tag = publisher.tag();
|
||||
let publisher_handle = publisher.name().clone();
|
||||
|
||||
let tag = match publisher.rfc8181() {
|
||||
Some(details) => Some(details.tag().clone()),
|
||||
None => return Err(Error::ClientUnitialised)
|
||||
};
|
||||
|
||||
|
||||
let handle = publisher.handle();
|
||||
let id_cert = my_id.id_cert().clone();
|
||||
let service_uri = publisher.service_uri().clone();
|
||||
|
||||
let sia_base = publisher.base_uri().clone();
|
||||
|
||||
Ok(
|
||||
RepositoryResponse::new(
|
||||
tag,
|
||||
publisher_handle,
|
||||
handle.clone(),
|
||||
id_cert,
|
||||
service_uri,
|
||||
sia_base,
|
||||
@@ -156,6 +162,9 @@ pub enum Error {
|
||||
|
||||
#[display(fmt="Identity of server is not initialised.")]
|
||||
Unitialised,
|
||||
|
||||
#[display(fmt="Identity of client is not initialised.")]
|
||||
ClientUnitialised,
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
@@ -188,6 +197,7 @@ impl From<builder::Error<softsigner::SignerError>> for Error {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::util::test;
|
||||
use daemon::publishers::Rfc8181PublisherDetails;
|
||||
|
||||
#[test]
|
||||
fn should_have_response_for_publisher() {
|
||||
@@ -202,17 +212,22 @@ mod tests {
|
||||
let base_uri = test::rsync_uri("rsync://host/module/alice/");
|
||||
let service_uri = test::http_uri("http://127.0.0.1:3000/rfc8181/alice");
|
||||
|
||||
let rfc8181 = Rfc8181PublisherDetails::new(tag, id_cert);
|
||||
|
||||
let publisher = Arc::new(Publisher::new(
|
||||
tag,
|
||||
name,
|
||||
"token".to_string(),
|
||||
base_uri,
|
||||
service_uri,
|
||||
id_cert
|
||||
Some(rfc8181)
|
||||
));
|
||||
|
||||
let rrdp_uri = test::http_uri("http://host/rrdp/");
|
||||
|
||||
responder.repository_response(publisher, rrdp_uri).unwrap();
|
||||
responder.repository_response(
|
||||
publisher,
|
||||
service_uri,
|
||||
rrdp_uri
|
||||
).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ fn client_publish_at_server() {
|
||||
res.text().unwrap().as_bytes()
|
||||
).unwrap();
|
||||
|
||||
|
||||
repo_res.validate().unwrap();
|
||||
client.process_repo_response(repo_res).unwrap();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user