Some more restructuring of the basic API. Closing #4 now, but surely this will still be refactored as more methods are added to the API.

This commit is contained in:
Tim Bruijnzeels
2019-01-02 16:19:41 +01:00
parent 3f613b800f
commit cfdff770b4
6 changed files with 205 additions and 13 deletions
+6 -2
View File
@@ -59,7 +59,9 @@ Currently we only provide an API for view the current state:
| Resource | Method | Action |
| ------------------------------ | -------- | ------------------------------- |
| /publishers | Get | List all current publishers |
| /publishers/{number}/response | Get | Get [repository response xml](https://tools.ietf.org/html/rfc8183#section-5.2.4)|
| /publishers/{handle} | Get | Get publisher details |
| /publishers/{handle}/id.cer | Get | Get publisher id certificate |
| /publishers/{handle}/response-xml | Get | Get [repository response xml](https://tools.ietf.org/html/rfc8183#section-5.2.4)|
For the moment publishers are configured by adding the publisher's ['publisher
@@ -79,7 +81,9 @@ logic wrapping around the API to ensure that things are then synchronised.
## UI
To add static resources, add to the 'static' folder and include static mapping at the end of src/pubd/httpd.rs. You should be able to get to them if you restart the server.
To add static resources, add to the 'static' folder and include static
mapping at the end of src/pubd/httpd.rs. You should be able to get to them if
you restart the server.
+123
View File
@@ -0,0 +1,123 @@
//! Support for the Json API
use std::sync::Arc;
use crate::ext_serde;
use crate::provisioning::publisher::Publisher;
use crate::rpki::uri;
//------------ Link ----------------------------------------------------------
/// Defines a link element to include as part of a links array in a Json
/// response.
#[derive(Clone, Debug, Serialize)]
pub struct Link<'a> {
rel: &'a str,
link: String
}
//------------ PublisherSummaryInfo ------------------------------------------
/// Defines a summary of publisher information to be used in the publisher
/// list.
#[derive(Clone, Debug, Serialize)]
pub struct PublisherSummaryInfo<'a> {
id: &'a str,
links: Vec<Link<'a>>
}
impl<'a> PublisherSummaryInfo<'a> {
pub fn from(
publisher: &'a Publisher,
path_publishers: &'a str
) -> PublisherSummaryInfo<'a> {
let id = publisher.name().as_str();
let mut links = Vec::new();
let response_link = Link {
rel: "response-xml",
link: format!("{}/{}/response-xml", path_publishers, id)
};
let self_link = Link {
rel: "self",
link: format!("{}/{}", path_publishers, id)
};
links.push(response_link);
links.push(self_link);
PublisherSummaryInfo {
id,
links
}
}
}
//------------ PublisherList -------------------------------------------------
/// This type represents a list of (all) current publishers to show in the API
#[derive(Clone, Debug, Serialize)]
pub struct PublisherList<'a> {
publishers: Vec<PublisherSummaryInfo<'a>>
}
impl<'a> PublisherList<'a> {
pub fn from(
publishers: &'a Vec<Arc<Publisher>>,
path_publishers: &'a str
) -> PublisherList<'a> {
let publishers: Vec<PublisherSummaryInfo> = publishers.iter().map(|p|
PublisherSummaryInfo::from(&p, path_publishers)
).collect();
PublisherList {
publishers
}
}
pub fn publishers(&self) -> &Vec<PublisherSummaryInfo> {
&self.publishers
}
}
//------------ PublisherDetails ----------------------------------------------
#[derive(Clone, Debug, Serialize)]
pub struct PublisherDetails<'a> {
name: &'a str,
#[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,
links: Vec<Link<'a>>
}
impl<'a> PublisherDetails<'a> {
pub fn from(
publisher: &'a Arc<Publisher>,
path_publishers: &'a str
) -> PublisherDetails<'a> {
let name = publisher.name().as_str();
let base_uri = publisher.base_uri();
let service_uri = publisher.service_uri();
let mut links = Vec::new();
links.push(Link {
rel: "response-xml",
link: format!("{}/{}/response-xml", path_publishers, name)
});
links.push(Link {
rel: "id-cert",
link: format!("{}/{}/id.cer", path_publishers, name)
});
PublisherDetails {
name,
base_uri,
service_uri,
links
}
}
}
+1
View File
@@ -26,6 +26,7 @@ extern crate xml as xmlrs;
extern crate ring;
extern crate untrusted;
pub mod api;
pub mod file;
pub mod provisioning;
pub mod pubc;
+62 -4
View File
@@ -18,9 +18,13 @@ use crate::pubd::https;
use crate::pubd::pubserver;
use crate::pubd::pubserver::PubServer;
use crate::remote::sigmsg::SignedMessage;
use api::PublisherList;
use api::PublisherDetails;
const NOT_FOUND: &'static [u8] = include_bytes!("../../static/html/404.html");
const PATH_PUBLISHERS: &'static str = "/api/v1/publishers";
//------------ PubServerApp --------------------------------------------------
pub struct PubServerApp(App<Arc<RwLock<PubServer>>>);
@@ -31,9 +35,15 @@ impl PubServerApp {
pub fn new(server: Arc<RwLock<PubServer>>) -> Self {
let app = App::with_state(server)
.middleware(middleware::Logger::default())
.resource("/api/v1/publishers", |r| {
.resource(PATH_PUBLISHERS, |r| {
r.f(Self::publishers)
})
.resource("/api/v1/publishers/{handle}", |r| {
r.f(Self::publisher_details)
})
.resource("/api/v1/publishers/{handle}/id.cer", |r| {
r.f(Self::id_cert)
})
.resource("/api/v1/publishers/{handle}/response-xml", |r| {
r.f(Self::repository_response)
})
@@ -156,7 +166,51 @@ impl PubServerApp {
let server: RwLockReadGuard<PubServer> = req.state().read().unwrap();
match server.publishers() {
Err(e) => Self::server_error(Error::ServerError(e)),
Ok(publishers) => Self::render_json(publishers)
Ok(publishers) => {
Self::render_json(
PublisherList::from(&publishers, PATH_PUBLISHERS)
)
}
}
}
/// Returns a json structure with publisher details
fn publisher_details(req: &HttpRequest) -> HttpResponse {
let server: RwLockReadGuard<PubServer> = req.state().read().unwrap();
match req.match_info().get("handle") {
None => Self::p404(req),
Some(handle) => {
match server.publisher(handle) {
Ok(None) => Self::p404(req),
Ok(Some(publisher)) => {
Self::render_json(
PublisherDetails::from(&publisher,
PATH_PUBLISHERS)
)
},
Err(e) => Self::server_error(Error::ServerError(e))
}
}
}
}
/// Returns the id.cer for a publisher
fn id_cert(req: &HttpRequest) -> HttpResponse {
let server: RwLockReadGuard<PubServer> = req.state().read().unwrap();
match req.match_info().get("handle") {
None => Self::p404(req),
Some(handle) => {
match server.publisher(handle) {
Ok(None) => Self::p404(req),
Ok(Some(publisher)) => {
let bytes = publisher.id_cert().to_bytes();
HttpResponse::Ok()
.content_type("application/pkix-cert")
.body(bytes)
},
Err(e) => Self::server_error(Error::ServerError(e))
}
}
}
}
@@ -169,7 +223,9 @@ impl PubServerApp {
Some(handle) => {
match server.repository_response(handle) {
Ok(res) => {
HttpResponse::Ok().body(res.encode_vec())
HttpResponse::Ok()
.content_type("application/xml")
.body(res.encode_vec())
},
Err(pubserver::Error::PublisherStoreError
(publisher_store::Error::UnknownPublisher(_))) => {
@@ -262,7 +318,9 @@ impl PubServerApp {
fn render_json<O: Serialize>(object: O) -> HttpResponse {
match serde_json::to_string(&object){
Ok(enc) => {
HttpResponse::Ok().body(enc)
HttpResponse::Ok()
.content_type("application/json")
.body(enc)
},
Err(e) => Self::server_error(Error::JsonError(e))
}
+9
View File
@@ -108,6 +108,15 @@ impl PubServer {
.map_err(|e| { Error::PublisherStoreError(e) })
}
/// Returns an option for a publisher.
pub fn publisher(
&self,
publisher_name: &str
) -> Result<Option<Arc<Publisher>>, Error> {
self.publisher_store.publisher(publisher_name)
.map_err(|e| Error::PublisherStoreError(e))
}
/// Returns a repository response for the given publisher.
///
/// Returns an error if the publisher is unknown.
+4 -7
View File
@@ -19,7 +19,6 @@ use rpubd::remote::oob::exchange::PublisherRequest;
use rpubd::remote::oob::exchange::RepositoryResponse;
use rpubd::file;
use rpubd::file::CurrentFile;
use rpubd::provisioning::publisher::Publisher;
use rpubd::pubc::client::PubClient;
use rpubd::pubd::config::Config;
use rpubd::pubd::http::PubServerApp;
@@ -68,13 +67,11 @@ fn client_publish_at_server() {
// XXX TODO: Find a better way to know the server is ready!
thread::sleep(time::Duration::from_millis(500));
// Should see one configured publisher
let mut res = reqwest::get("http://localhost:3000/publishers").unwrap();
let pl: Vec<Publisher> = serde_json::from_str(&res.text().unwrap()).unwrap();
assert_eq!(1, pl.len());
// Should get repository response for alice
let mut res = reqwest::get("http://localhost:3000/publishers/alice").unwrap();
let mut res = reqwest::get
("http://localhost:3000/api/v1/publishers/alice/response-xml")
.unwrap();
let repo_res = RepositoryResponse::decode(
res.text().unwrap().as_bytes()
).unwrap();