From 5dd37297d7fe2538d1db52c80a08365e723ce6c8 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Wed, 28 Sep 2022 22:14:02 +0200 Subject: [PATCH] #288 #489 Register Endpoint and true URLs --- lib/Cargo.toml | 2 +- lib/src/collections.rs | 2 +- lib/src/commit.rs | 2 +- lib/src/db.rs | 28 +++++++------ lib/src/db/query_index.rs | 6 +-- lib/src/endpoints.rs | 1 + lib/src/plugins/mod.rs | 1 + lib/src/plugins/register.rs | 72 +++++++++++++++++++++++++++++++++ lib/src/populate.rs | 71 +++++++++++++++++++++----------- lib/src/resources.rs | 8 +--- lib/src/store.rs | 12 ++++-- lib/src/storelike.rs | 46 ++++++++++++++++----- lib/src/urls.rs | 4 +- server/src/appstate.rs | 4 +- server/src/bin.rs | 2 +- server/src/commit_monitor.rs | 5 ++- server/src/handlers/commit.rs | 8 +--- server/src/handlers/resource.rs | 44 +++++++++++--------- server/src/helpers.rs | 1 + server/src/https.rs | 1 + 20 files changed, 225 insertions(+), 95 deletions(-) create mode 100644 lib/src/plugins/register.rs diff --git a/lib/Cargo.toml b/lib/Cargo.toml index ebcb23823..f8417fe36 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -21,6 +21,7 @@ bincode = {version = "1", optional = true} directories = {version = ">= 2, < 5", optional = true} html2md = {version = "0.2.13", optional = true} kuchiki = {version = "0.8.1", optional = true} +lazy_static = "1" lol_html = {version = "0.3.1", optional = true} rand = {version = "0.8"} regex = "1" @@ -39,7 +40,6 @@ urlencoding = "2" [dev-dependencies] criterion = "0.3" iai = "0.1" -lazy_static = "1" ntest = "0.7" [features] diff --git a/lib/src/collections.rs b/lib/src/collections.rs index 35a4c3670..5347634f5 100644 --- a/lib/src/collections.rs +++ b/lib/src/collections.rs @@ -427,7 +427,7 @@ pub fn create_collection_resource_for_class( // Let the Collections collection be the top level item let parent = if class.subject == urls::COLLECTION { - drive + drive.to_string() } else { format!("{}/collections", drive) }; diff --git a/lib/src/commit.rs b/lib/src/commit.rs index 78ce71b72..eb05e49a7 100644 --- a/lib/src/commit.rs +++ b/lib/src/commit.rs @@ -172,7 +172,7 @@ impl Commit { let default_parent = store.get_self_url().ok_or("There is no self_url set, and no parent in the Commit. The commit can not be applied.")?; resource_old.set_propval( urls::PARENT.into(), - Value::AtomicUrl(default_parent), + Value::AtomicUrl(default_parent.to_string()), store, )?; } diff --git a/lib/src/db.rs b/lib/src/db.rs index 440f9ff8d..01c8002c5 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -13,7 +13,8 @@ use std::{ sync::{Arc, Mutex}, }; -use tracing::{info, instrument}; +use tracing::instrument; +use url::Url; use crate::{ atoms::IndexAtom, @@ -75,7 +76,7 @@ pub struct Db { /// A list of all the Collections currently being used. Is used to update `query_index`. watched_queries: sled::Tree, /// The address where the db will be hosted, e.g. http://localhost/ - server_url: String, + server_url: Url, /// Endpoints are checked whenever a resource is requested. They calculate (some properties of) the resource and return it. endpoints: Vec, /// Function called whenever a Commit is applied. @@ -192,6 +193,7 @@ impl Db { } fn map_sled_item_to_resource( + &self, item: Result<(sled::IVec, sled::IVec), sled::Error>, self_url: String, include_external: bool, @@ -199,7 +201,7 @@ impl Db { let (subject, resource_bin) = item.expect(DB_CORRUPT_MSG); let subject: String = String::from_utf8_lossy(&subject).to_string(); - if !include_external && !subject.starts_with(&self_url) { + if !include_external && self.is_external_subject(&subject).ok()? { return None; } @@ -307,14 +309,14 @@ impl Storelike for Db { Ok(()) } - fn get_server_url(&self) -> &str { + fn get_server_url(&self) -> &Url { &self.server_url } - // Since the DB is often also the server, this should make sense. - // Some edge cases might appear later on (e.g. a slave DB that only stores copies?) - fn get_self_url(&self) -> Option { - Some(self.get_server_url().into()) + fn get_self_url(&self) -> Option<&Url> { + // Since the DB is often also the server, this should make sense. + // Some edge cases might appear later on (e.g. a slave DB that only stores copies?) + Some(self.get_server_url()) } fn get_default_agent(&self) -> AtomicResult { @@ -486,7 +488,7 @@ impl Storelike for Db { // Maybe make this optional? q_filter.watch(self)?; - info!(filter = ?q_filter, "Building query index"); + tracing::info!(filter = ?q_filter, "Building query index"); let atoms: IndexIterator = match (&q.property, q.value.as_ref()) { (Some(prop), val) => find_in_prop_val_sub_index(self, prop, val), @@ -525,7 +527,7 @@ impl Storelike for Db { .expect("No self URL set, is required in DB"); let result = self.resources.into_iter().filter_map(move |item| { - Db::map_sled_item_to_resource(item, self_url.clone(), include_external) + Db::map_sled_item_to_resource(self, item, self_url.to_string(), include_external) }); Box::new(result) @@ -538,9 +540,11 @@ impl Storelike for Db { // This is a potentially expensive operation, but is needed to make TPF queries work with the models created in here self.build_index(true) .map_err(|e| format!("Failed to build index. {}", e))?; - crate::populate::create_drive(self) + let default_agent = self + .get_default_agent() + .map_err(|_| "No default agent found")?; + crate::populate::create_drive(self, None, &default_agent.subject, true) .map_err(|e| format!("Failed to create drive. {}", e))?; - crate::populate::set_drive_rights(self, true)?; crate::populate::populate_collections(self) .map_err(|e| format!("Failed to populate collections. {}", e))?; crate::populate::populate_endpoints(self) diff --git a/lib/src/db/query_index.rs b/lib/src/db/query_index.rs index d28582542..dce7e537a 100644 --- a/lib/src/db/query_index.rs +++ b/lib/src/db/query_index.rs @@ -94,10 +94,6 @@ pub fn query_indexed(store: &Db, q: &Query) -> AtomicResult { let mut resources = Vec::new(); let mut count = 0; - let self_url = store - .get_self_url() - .ok_or("No self_url set, required for Queries")?; - let limit = if let Some(limit) = q.limit { limit } else { @@ -114,7 +110,7 @@ pub fn query_indexed(store: &Db, q: &Query) -> AtomicResult { let (_q_filter, _val, subject) = parse_collection_members_key(&k)?; // If no external resources should be included, skip this one if it's an external resource - if !q.include_external && !subject.starts_with(&self_url) { + if !q.include_external && store.is_external_subject(subject)? { continue; } diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index af1e000c7..32468acff 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -49,6 +49,7 @@ pub fn default_endpoints() -> Vec { plugins::path::path_endpoint(), plugins::search::search_endpoint(), plugins::files::upload_endpoint(), + plugins::register::register_endpoint(), #[cfg(feature = "html")] plugins::bookmark::bookmark_endpoint(), ] diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index 9ad0b14a6..39de27da0 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -43,5 +43,6 @@ pub mod invite; pub mod bookmark; pub mod files; pub mod path; +pub mod register; pub mod search; pub mod versioning; diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs new file mode 100644 index 000000000..6aad93b16 --- /dev/null +++ b/lib/src/plugins/register.rs @@ -0,0 +1,72 @@ +//! Creates a new Drive and optionally also an Agent. + +use crate::{agents::Agent, endpoints::Endpoint, errors::AtomicResult, urls, Resource, Storelike}; + +pub fn register_endpoint() -> Endpoint { + Endpoint { + path: "/register".to_string(), + params: [ + urls::INVITE_PUBKEY.to_string(), + urls::NAME.to_string(), + ].into(), + description: "Allows new users to easily, in one request, make both an Agent and a Drive. This drive will be created at the subdomain of `name`.".to_string(), + shortname: "register".to_string(), + handle: Some(construct_register_redirect), + } +} + +#[tracing::instrument(skip(store))] +pub fn construct_register_redirect( + url: url::Url, + store: &impl Storelike, + for_agent: Option<&str>, +) -> AtomicResult { + let requested_subject = url.to_string(); + let mut pub_key = None; + let mut name_option = None; + for (k, v) in url.query_pairs() { + match k.as_ref() { + "public-key" | urls::INVITE_PUBKEY => pub_key = Some(v.to_string()), + "name" | urls::NAME => name_option = Some(v.to_string()), + _ => {} + } + } + if pub_key.is_none() && name_option.is_none() { + return register_endpoint().to_resource(store); + } + + let name = if let Some(n) = name_option { + n + } else { + return Err("No name provided".into()); + }; + + let mut new_agent = None; + + let drive_creator_agent: String = if let Some(key) = pub_key { + let new = Agent::new_from_public_key(store, &key)?.subject; + new_agent = Some(new.clone()); + new + } else if let Some(agent) = for_agent { + agent.to_string() + } else { + return Err("No `public-key` provided".into()); + }; + + // Create the new Drive + let drive = crate::populate::create_drive(store, Some(&name), &drive_creator_agent, false)?; + + // Construct the Redirect Resource, which might provide the Client with a Subject for his Agent. + let mut redirect = Resource::new_instance(urls::REDIRECT, store)?; + redirect.set_propval_string(urls::DESTINATION.into(), drive.get_subject(), store)?; + if let Some(agent) = new_agent { + redirect.set_propval( + urls::REDIRECT_AGENT.into(), + crate::Value::AtomicUrl(agent), + store, + )?; + } + // The front-end requires the @id to be the same as requested + redirect.set_subject(requested_subject); + Ok(redirect) +} diff --git a/lib/src/populate.rs b/lib/src/populate.rs index 9f6c25239..33b80c8b1 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -151,32 +151,49 @@ pub fn populate_base_models(store: &impl Storelike) -> AtomicResult<()> { Ok(()) } -/// Creates a Drive resource at the base URL. Does not set rights. Use set_drive_rights for that. -pub fn create_drive(store: &impl Storelike) -> AtomicResult<()> { - let self_url = store - .get_self_url() - .ok_or("No self_url set, cannot populate store with Drive")?; - let mut drive = store.get_resource_new(&self_url); +/// Creates a Drive resource at the base URL if no name is passed. +#[tracing::instrument(skip(store), level = "info")] +pub fn create_drive( + store: &impl Storelike, + drive_name: Option<&str>, + for_agent: &str, + public_read: bool, +) -> AtomicResult { + let mut self_url = if let Some(url) = store.get_self_url() { + url.to_owned() + } else { + return Err("No self URL set. Cannot create drive.".into()); + }; + let drive_subject: String = if let Some(name) = drive_name { + // Let's make a subdomain + let host = self_url.host().expect("No host in server_url"); + let subdomain_host = format!("{}.{}", name, host); + self_url.set_host(Some(&subdomain_host))?; + self_url.to_string() + } else { + self_url.to_string() + }; + + let mut drive = if drive_name.is_some() { + if store.get_resource(&drive_subject).is_ok() { + return Err("Drive URL is already taken".into()); + } + Resource::new(drive_subject) + } else { + // Only for the base URL (of no drive name is passed), we should not check if the drive exists. + // This is because we use `create_drive` in the `--initialize` command. + store.get_resource_new(&drive_subject) + }; drive.set_class(urls::DRIVE); - let server_url = url::Url::parse(store.get_server_url())?; drive.set_propval_string( urls::NAME.into(), - server_url.host_str().ok_or("Can't use current base URL")?, + drive_name.unwrap_or_else(|| self_url.host_str().unwrap()), store, )?; - drive.save_locally(store)?; - Ok(()) -} -/// Adds rights to the default agent to the Drive resource (at the base URL). Optionally give Public Read rights. -pub fn set_drive_rights(store: &impl Storelike, public_read: bool) -> AtomicResult<()> { - // Now let's add the agent as the Root user and provide write access - let mut drive = store.get_resource(store.get_server_url())?; - let write_agent = store.get_default_agent()?.subject; - let read_agent = write_agent.clone(); - - drive.push_propval(urls::WRITE, write_agent.into(), true)?; - drive.push_propval(urls::READ, read_agent.into(), true)?; + // Set rights + drive.push_propval(urls::WRITE, for_agent.into(), true)?; + drive.push_propval(urls::READ, for_agent.into(), true)?; if public_read { drive.push_propval(urls::READ, urls::PUBLIC_AGENT.into(), true)?; } @@ -189,8 +206,10 @@ Register your Agent by visiting [`/setup`]({}/setup). After that, edit this page Note that, by default, all resources are `public`. You can edit this by opening the context menu (the three dots in the navigation bar), and going to `share`. "#, store.get_server_url()), store)?; } + drive.save_locally(store)?; - Ok(()) + + Ok(drive) } /// Imports the Atomic Data Core items (the entire atomicdata.dev Ontology / Vocabulary) @@ -251,9 +270,13 @@ pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { let base = store .get_self_url() .ok_or("No self URL in this Store - required for populating importer")?; - let mut importer = Resource::new(urls::construct_path_import(&base)); + let mut importer = Resource::new(urls::construct_path_import(base)); importer.set_class(urls::IMPORTER); - importer.set_propval(urls::PARENT.into(), Value::AtomicUrl(base), store)?; + importer.set_propval( + urls::PARENT.into(), + Value::AtomicUrl(base.to_string()), + store, + )?; importer.set_propval(urls::NAME.into(), Value::String("Import".into()), store)?; importer.save_locally(store)?; Ok(()) @@ -264,7 +287,7 @@ pub fn populate_importer(store: &crate::Db) -> AtomicResult<()> { /// Useful for helping a new user get started. pub fn populate_sidebar_items(store: &crate::Db) -> AtomicResult<()> { let base = store.get_self_url().ok_or("No self_url")?; - let mut drive = store.get_resource(&base)?; + let mut drive = store.get_resource(base.as_str())?; let arr = vec![ format!("{}/setup", base), format!("{}/import", base), diff --git a/lib/src/resources.rs b/lib/src/resources.rs index e72d2dad5..425df0540 100644 --- a/lib/src/resources.rs +++ b/lib/src/resources.rs @@ -320,13 +320,7 @@ impl Resource { let commit_builder = self.get_commit_builder().clone(); let commit = commit_builder.sign(&agent, store, self)?; // If the current client is a server, and the subject is hosted here, don't post - let should_post = if let Some(self_url) = store.get_self_url() { - !self.subject.starts_with(&self_url) - } else { - // Current client is not a server, has no own persisted store - true - }; - if should_post { + if store.is_external_subject(&commit.subject)? { crate::client::post_commit(&commit, store)?; } let opts = CommitOpts { diff --git a/lib/src/store.rs b/lib/src/store.rs index d3e5c46a3..44fed4062 100644 --- a/lib/src/store.rs +++ b/lib/src/store.rs @@ -15,6 +15,10 @@ pub struct Store { default_agent: Arc>>, } +lazy_static::lazy_static! { + static ref LOCAL_STORE_URL: Url = Url::parse("local:store").unwrap(); +} + impl Store { /// Creates an empty Store. /// Run `.populate()` to get useful standard models loaded into your store. @@ -180,14 +184,14 @@ impl Storelike for Store { ) } - fn get_server_url(&self) -> &str { + fn get_server_url(&self) -> &Url { // TODO Should be implemented later when companion functionality is here // https://github.com/atomicdata-dev/atomic-data-rust/issues/6 - "local:store" + &LOCAL_STORE_URL } - fn get_self_url(&self) -> Option { - Some(self.get_server_url().into()) + fn get_self_url(&self) -> Option<&Url> { + Some(self.get_server_url()) } fn get_default_agent(&self) -> AtomicResult { diff --git a/lib/src/storelike.rs b/lib/src/storelike.rs index aa90bac10..662ac63f1 100644 --- a/lib/src/storelike.rs +++ b/lib/src/storelike.rs @@ -1,5 +1,7 @@ //! The Storelike Trait contains many useful methods for maniupulting / retrieving data. +use url::Url; + use crate::{ agents::Agent, commit::CommitResponse, @@ -84,12 +86,12 @@ pub trait Storelike: Sized { /// E.g. `https://example.com` /// This is where deltas should be sent to. /// Also useful for Subject URL generation. - fn get_server_url(&self) -> &str; + fn get_server_url(&self) -> &Url; /// Returns the root URL where this instance of the store is hosted. /// Should return `None` if this is simply a client and not a server. /// E.g. `https://example.com` - fn get_self_url(&self) -> Option { + fn get_self_url(&self) -> Option<&Url> { None } @@ -200,15 +202,15 @@ pub trait Storelike: Sized { fn handle_commit(&self, _commit_response: &CommitResponse) {} fn handle_not_found(&self, subject: &str, error: AtomicError) -> AtomicResult { - if let Some(self_url) = self.get_self_url() { - if subject.starts_with(&self_url) { - return Err(AtomicError::not_found(format!( - "Failed to retrieve locally: '{}'. {}", - subject, error - ))); - } + // This does not work for subdomains + if self.is_external_subject(subject)? { + self.fetch_resource(subject) + } else { + Err(AtomicError::not_found(format!( + "Failed to retrieve locally: '{}'. {}", + subject, error + ))) } - self.fetch_resource(subject) } /// Imports a JSON-AD string, returns the amount of imported resources. @@ -219,6 +221,30 @@ pub trait Storelike: Sized { Ok(len) } + /// Checks if the URL of some resource is owned by some external store. + /// If true, then the Subject points to a different server. + /// If you're using `Storelike` on something that does not persist (e.g. a client app), + /// the answer should always be `true`. + fn is_external_subject(&self, subject: &str) -> AtomicResult { + if let Some(self_url) = self.get_self_url() { + if subject.starts_with(&self_url.as_str()) { + return Ok(false); + } else { + let subject_url = url::Url::parse(subject)?; + let subject_host = subject_url.host().ok_or_else(|| { + AtomicError::not_found(format!("Subject URL has no host: {}", subject)) + })?; + let self_host = self_url.host().ok_or_else(|| { + AtomicError::not_found(format!("Self URL has no host: {}", self_url)) + })?; + if subject_host == self_host { + return Ok(false); + } + } + } + Ok(true) + } + /// Removes a resource from the store. Errors if not present. fn remove_resource(&self, subject: &str) -> AtomicResult<()>; diff --git a/lib/src/urls.rs b/lib/src/urls.rs index 04d2a079b..a97598a4d 100644 --- a/lib/src/urls.rs +++ b/lib/src/urls.rs @@ -1,5 +1,7 @@ //! Contains some of the most important Atomic Data URLs. +use url::Url; + // Classes pub const CLASS: &str = "https://atomicdata.dev/classes/Class"; pub const PROPERTY: &str = "https://atomicdata.dev/classes/Property"; @@ -133,7 +135,7 @@ pub const DELETE: &str = "https://atomicdata.dev/methods/delete"; pub const PUBLIC_AGENT: &str = "https://atomicdata.dev/agents/publicAgent"; // Paths -pub fn construct_path_import(base: &str) -> String { +pub fn construct_path_import(base: &Url) -> String { format!("{base}{PATH_IMPORT}") } diff --git a/server/src/appstate.rs b/server/src/appstate.rs index fec80cde9..ca43a2b22 100644 --- a/server/src/appstate.rs +++ b/server/src/appstate.rs @@ -173,12 +173,12 @@ fn set_up_initial_invite(store: &impl Storelike) -> AtomicServerResult<()> { )?; invite.set_propval( atomic_lib::urls::TARGET.into(), - atomic_lib::Value::AtomicUrl(store.get_server_url().into()), + atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()), store, )?; invite.set_propval( atomic_lib::urls::PARENT.into(), - atomic_lib::Value::AtomicUrl(store.get_server_url().into()), + atomic_lib::Value::AtomicUrl(store.get_server_url().to_string()), store, )?; invite.set_propval( diff --git a/server/src/bin.rs b/server/src/bin.rs index e0d51dbef..a73abaa30 100644 --- a/server/src/bin.rs +++ b/server/src/bin.rs @@ -69,7 +69,7 @@ async fn main_wrapped() -> errors::AtomicServerResult<()> { let importer_subject = if let Some(i) = &import_opts.parent { i.into() } else { - urls::construct_path_import(&appstate.store.get_self_url().expect("No self url")) + urls::construct_path_import(appstate.store.get_self_url().expect("No self url")) }; let parse_opts = atomic_lib::parse::ParseOpts { importer: Some(importer_subject), diff --git a/server/src/commit_monitor.rs b/server/src/commit_monitor.rs index db638b284..626421489 100644 --- a/server/src/commit_monitor.rs +++ b/server/src/commit_monitor.rs @@ -56,8 +56,9 @@ impl Handler for CommitMonitor { fields(to = %msg.subject, agent = %msg.agent) )] fn handle(&mut self, msg: Subscribe, _ctx: &mut Context) { - // check if the agent has the rights to subscribe to this resource - if !msg.subject.starts_with(&self.store.get_self_url().unwrap()) { + // check if the agent has the rights to subscribe to this resourceif let Some(self_url) = self.get_self_url() { + + if self.store.is_external_subject(&msg.subject).unwrap_or(true) { tracing::warn!("can't subscribe to external resource"); return; } diff --git a/server/src/handlers/commit.rs b/server/src/handlers/commit.rs index c477419c9..5200ffb0b 100644 --- a/server/src/handlers/commit.rs +++ b/server/src/handlers/commit.rs @@ -13,12 +13,8 @@ pub async fn post_commit( let mut builder = HttpResponse::Ok(); let incoming_commit_resource = parse_json_ad_commit_resource(&body, store)?; let incoming_commit = Commit::from_resource(incoming_commit_resource)?; - if !incoming_commit.subject.contains( - &store - .get_self_url() - .ok_or("Cannot apply commits to this store. No self_url is set.")?, - ) { - return Err("Subject of commit should be sent to other domain - this store can not own this resource.".into()); + if store.is_external_subject(&incoming_commit.subject)? { + return Err("Subject of commit is external, and should be sent to its origin domain. This store can not own this resource. See https://github.com/atomicdata-dev/atomic-data-rust/issues/509".into()); } let opts = CommitOpts { validate_schema: true, diff --git a/server/src/handlers/resource.rs b/server/src/handlers/resource.rs index 431cc2282..1cb10b57b 100644 --- a/server/src/handlers/resource.rs +++ b/server/src/handlers/resource.rs @@ -1,10 +1,6 @@ use crate::{ - appstate::AppState, - content_types::get_accept, - content_types::ContentType, - errors::AtomicServerResult, - helpers::{get_client_agent, try_extension}, - timer::Timer, + appstate::AppState, content_types::get_accept, content_types::ContentType, + errors::AtomicServerResult, helpers::get_client_agent, timer::Timer, }; use actix_web::{web, HttpResponse}; use atomic_lib::Storelike; @@ -16,39 +12,51 @@ pub async fn handle_get_resource( path: Option>, appstate: web::Data, req: actix_web::HttpRequest, + conn: actix_web::dev::ConnectionInfo, ) -> AtomicServerResult { let mut timer = Timer::new(); + let domain = &appstate.config.opts.domain; + let host = conn.host(); + let find = host.find(domain); + let subdomain = if let Some(index) = find { + if index == 0 { + None + } else { + Some(host[0..index - 1].to_string()) + } + } else { + panic!("Wrong domain! A requested URL did not contain the host for this domain. This should not be able to happen."); + }; let headers = req.headers(); - let mut content_type = get_accept(headers); + let content_type = get_accept(headers); let server_url = &appstate.config.server_url; + println!("server_url: {}", server_url); // Get the subject from the path, or return the home URL let subject = if let Some(subj_end) = path { - let mut subj_end_string = subj_end.as_str(); + let subj_end_string = subj_end.as_str(); // If the request is for the root, return the home URL - if subj_end_string.is_empty() { + if subj_end.as_str().is_empty() { server_url.to_string() } else { - if content_type == ContentType::Html { - if let Some((ext, path)) = try_extension(subj_end_string) { - content_type = ext; - subj_end_string = path; - } - } - // Check extensions and set datatype. Harder than it looks to get right... // This might not be the best way of creating the subject. But I can't access the full URL from any actix stuff! let querystring = if req.query_string().is_empty() { "".to_string() } else { format!("?{}", req.query_string()) }; - let subject = format!("{}/{}{}", server_url, subj_end_string, querystring); - subject + if let Some(sd) = subdomain { + // TODO: ONLY WORKS IN DEVELOPMENT, HACKY + format!("http://{}.localhost/{}{}", sd, subj_end_string, querystring) + } else { + format!("{}/{}{}", server_url, subj_end_string, querystring) + } } } else { // There is no end string, so It's the root of the URL, the base URL! String::from(server_url) }; + println!("subject: {}", subject); let store = &appstate.store; timer.add("parse_headers"); diff --git a/server/src/helpers.rs b/server/src/helpers.rs index 30d5424dd..513545cab 100644 --- a/server/src/helpers.rs +++ b/server/src/helpers.rs @@ -120,6 +120,7 @@ pub fn get_auth( /// Checks for authentication headers and returns Some agent's subject if everything is well. /// Skips these checks in public_mode and returns Ok(None). +/// Returns the Agent's subject or the Public Agent. #[tracing::instrument(skip(appstate))] pub fn get_client_agent( headers: &HeaderMap, diff --git a/server/src/https.rs b/server/src/https.rs index 12586ab3e..0dac4cfdf 100644 --- a/server/src/https.rs +++ b/server/src/https.rs @@ -100,6 +100,7 @@ pub fn request_cert(config: &crate::config::Config) -> Result<(), Error> { let acc = dir.account(&email)?; // Allow all subdomains for this same certificate. Not sure if this works! + // https://github.com/atomicdata-dev/atomic-data-rust/issues/502 let wildcard_domain = format!("*.{}", &config.opts.domain); // Order a new TLS certificate for a domain. let mut ord_new = acc.new_order(&wildcard_domain, &[])?;