From 6c322c812ec1adc5fa5b5da7ebc5357d00e44315 Mon Sep 17 00:00:00 2001 From: Joep Meindertsma Date: Tue, 3 Jan 2023 12:48:47 +0100 Subject: [PATCH] Refactor endpoints, update JSON --- CHANGELOG.md | 2 +- lib/defaults/default_base_models.json | 18 ++++++++---- lib/defaults/default_store.json | 14 +++++++-- lib/src/db.rs | 3 +- lib/src/endpoints.rs | 16 +--------- .../{reset_pubkey.rs => add_pubkey.rs} | 15 ++++++---- lib/src/plugins/mod.rs | 29 +++++++++++++++---- lib/src/plugins/register.rs | 1 + lib/src/populate.rs | 2 +- 9 files changed, 63 insertions(+), 37 deletions(-) rename lib/src/plugins/{reset_pubkey.rs => add_pubkey.rs} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfecbf8d2..2c562724b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Changes to JS assets are not included here, but in [`atomic-data-browser`'s CHAN ## UNRELEASED -- New sign up / register flow. Add `/register` Endpoint #489 #254 +- New sign up / register flow. Add `/register`, `/confirm-email`, `/add-public-key` endpoints #489 #254 - Add multi-tenancy support. Users can create their own `Drives` on subdomains. #288 - Refactor URLs. `store.self_url()` returns an `AtomicUrl`, which provides methods to easily add paths, find subdomains and more. - Add support for subdomains, use a Wildcard TLS certificate #502 diff --git a/lib/defaults/default_base_models.json b/lib/defaults/default_base_models.json index 4731f33d4..f5262cbef 100644 --- a/lib/defaults/default_base_models.json +++ b/lib/defaults/default_base_models.json @@ -78,17 +78,23 @@ }, { "@id": "https://atomicdata.dev/classes/Property", - "https://atomicdata.dev/properties/description": "A Resource that should redirect the browser to a new location. It can also set a `redirectAgent`, which is used in Invites to create an Agent Resource on the Server from a Public Key that the user posesses. See the [Invite docs](https://docs.atomicdata.dev/invitations.html).", + "https://atomicdata.dev/properties/description": "A Property is a single field in a Class. It's the thing that a property field in an Atom points to. An example is `birthdate`. An instance of Property requires various Properties, most notably a `datatype` (e.g. `string` or `integer`), a human readable `description` (such as the thing you're reading), and a `shortname`.", "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Class" ], - "https://atomicdata.dev/properties/requires": [ - "https://atomicdata.dev/properties/destination" - ], + "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/classes", "https://atomicdata.dev/properties/recommends": [ - "https://atomicdata.dev/properties/invite/redirectAgent" + "https://atomicdata.dev/properties/classtype", + "https://atomicdata.dev/properties/isDynamic", + "https://atomicdata.dev/properties/isLocked", + "https://atomicdata.dev/properties/allowsOnly" + ], + "https://atomicdata.dev/properties/requires": [ + "https://atomicdata.dev/properties/shortname", + "https://atomicdata.dev/properties/datatype", + "https://atomicdata.dev/properties/description" ], - "https://atomicdata.dev/properties/shortname": "redirect" + "https://atomicdata.dev/properties/shortname": "property" }, { "@id": "https://atomicdata.dev/classes/Class", diff --git a/lib/defaults/default_store.json b/lib/defaults/default_store.json index 44a1be2d0..4ae70a6d6 100644 --- a/lib/defaults/default_store.json +++ b/lib/defaults/default_store.json @@ -749,10 +749,9 @@ "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Property" ], - "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/VB3gtWMkysTX5hKjbYjIM1hfVGPywT3pEPL8c7NwaUAJID6RzptGRPzmix8aKKDeb8Pj1WFv0UPV0YVPxcduBg==", "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", "https://atomicdata.dev/properties/shortname": "sub-resources" - }, + },ยด { "@id": "https://atomicdata.dev/properties/tags", "https://atomicdata.dev/properties/classtype": "https://atomicdata.dev/classes/Tag", @@ -761,10 +760,19 @@ "https://atomicdata.dev/properties/isA": [ "https://atomicdata.dev/classes/Property" ], - "https://atomicdata.dev/properties/lastCommit": "https://atomicdata.dev/commits/fS0krtm1wDk0lodH0psnUKmBHBMKLuxnjkd7E7QbkzDk/irQ43gNW3lWxkwQj58ZNg6rUAUMDGJrLy1X3cHwBQ==", "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", "https://atomicdata.dev/properties/shortname": "tags" }, + { + "@id": "https://atomicdata.dev/properties/token", + "https://atomicdata.dev/properties/datatype": "https://atomicdata.dev/datatypes/string", + "https://atomicdata.dev/properties/description": "A server-generated string that should not mean anything to the client. It could be a JWT token, or something else.", + "https://atomicdata.dev/properties/isA": [ + "https://atomicdata.dev/classes/Property" + ], + "https://atomicdata.dev/properties/parent": "https://atomicdata.dev/properties", + "https://atomicdata.dev/properties/shortname": "token" + }, { "@id": "https://atomicdata.dev/properties/write", "https://atomicdata.dev/properties/classtype": "https://atomicdata.dev/classes/Agent", diff --git a/lib/src/db.rs b/lib/src/db.rs index 0668d5a2e..d36273488 100644 --- a/lib/src/db.rs +++ b/lib/src/db.rs @@ -24,8 +24,9 @@ use crate::{ commit::CommitResponse, db::{query_index::NO_VALUE, val_prop_sub_index::find_in_val_prop_sub_index}, email::{self, MailMessage}, - endpoints::{default_endpoints, Endpoint}, + endpoints::Endpoint, errors::{AtomicError, AtomicResult}, + plugins::default_endpoints, query::QueryResult, resources::PropVals, storelike::Storelike, diff --git a/lib/src/endpoints.rs b/lib/src/endpoints.rs index 9f3090251..39af326dd 100644 --- a/lib/src/endpoints.rs +++ b/lib/src/endpoints.rs @@ -3,7 +3,7 @@ //! Examples of endpoints are versions for resources, or (pages for) collections. //! See https://docs.atomicdata.dev/endpoints.html or https://atomicdata.dev/classes/Endpoint -use crate::{errors::AtomicResult, plugins, urls, Db, Resource, Storelike, Value}; +use crate::{errors::AtomicResult, urls, Db, Resource, Storelike, Value}; /// The function that is called when the request matches the path type HandleFunction = @@ -54,17 +54,3 @@ impl std::fmt::Debug for Endpoint { .finish() } } - -pub fn default_endpoints() -> Vec { - vec![ - plugins::versioning::version_endpoint(), - plugins::versioning::all_versions_endpoint(), - plugins::path::path_endpoint(), - plugins::search::search_endpoint(), - plugins::files::upload_endpoint(), - plugins::register::register_endpoint(), - plugins::register::confirm_email_endpoint(), - #[cfg(feature = "html")] - plugins::bookmark::bookmark_endpoint(), - ] -} diff --git a/lib/src/plugins/reset_pubkey.rs b/lib/src/plugins/add_pubkey.rs similarity index 90% rename from lib/src/plugins/reset_pubkey.rs rename to lib/src/plugins/add_pubkey.rs index 9f5abcaa0..658537469 100644 --- a/lib/src/plugins/reset_pubkey.rs +++ b/lib/src/plugins/add_pubkey.rs @@ -46,9 +46,8 @@ pub fn handle_request_email_pubkey( ) -> AtomicResult { let mut email_option: Option = None; for (k, v) in url.query_pairs() { - match k.as_ref() { - "email" => email_option = Some(EmailAddress::new(v.to_string())?), - _ => {} + if let "email" = k.as_ref() { + email_option = Some(EmailAddress::new(v.to_string())?) } } // by default just return the Endpoint @@ -57,7 +56,12 @@ pub fn handle_request_email_pubkey( }; // Find the agent by their email - let agent = Agent::from_email(&email.to_string(), store)?; + let agent = match Agent::from_email(&email.to_string(), store) { + Ok(a) => a, + // If we can't find the agent, we should still return a `success` response, + // in order to prevent users to know that the email exists. + Err(_) => return return_success(), + }; // send the user an e-mail to confirm sign up let store_clone = store.clone(); @@ -108,12 +112,13 @@ pub fn handle_confirm_add_pubkey( _ => {} } } - let pubkey = pubkey_option.ok_or("No public-key provided")?; let Some(token) = token_opt else { return confirm_add_pubkey().to_resource(store); }; + let pubkey = pubkey_option.ok_or("No public-key provided")?; + // Parse and verify the JWT token let confirmation = crate::token::verify_claim::(store, &token)?.custom; diff --git a/lib/src/plugins/mod.rs b/lib/src/plugins/mod.rs index f22cb98ba..fef3a5ae4 100644 --- a/lib/src/plugins/mod.rs +++ b/lib/src/plugins/mod.rs @@ -6,7 +6,7 @@ Plugins can have functions that are called at specific moments by Atomic-Server. For example: -- Before returning a Resource. These are either Endpoints or Class Extenders. +- Before returning a Resource. These are either [Endpoint]s or Class Extenders. - Before applying a Commit. In the long term, these plugins will probably be powered by WASM and can be extended at runtime. @@ -16,13 +16,13 @@ However, they are designed in such a way that they have a limited scope and a cl ## Extending resources There are two ways of extending / modifying a Resource. -Endpoints are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route. +[Endpoint]s are great for APIs that have a fixed route, and Class Extenders are great for APIs that don't have a fixed route. Endpoints are easier to generate from Rust, and will be available the second a server is Running. -### Endpoints +### [Endpoint]s Resources that typically parse query parameters and return a dynamic resource. -When adding an endpoint, add it to the list of endpoints in [lib/src/endpoints.rs] +When adding an endpoint, add it to the list of [default_endpoints] in this file. Endpoints are all instances of the [crate] class. They are presented in the UI as a form. @@ -31,22 +31,41 @@ They are presented in the UI as a form. Similar to Endpoints, Class Extenders can modify their contents before creating a response. Contrary to Endpoints, these can be any type of Class. They are used for performing custom queries, or calculating dynamic attributes. +Add these by registering the handler at [crate::db::Db::get_resource_extended]. */ +use crate::endpoints::Endpoint; + // Class Extenders pub mod chatroom; pub mod importer; pub mod invite; // Endpoints +pub mod add_pubkey; #[cfg(feature = "html")] pub mod bookmark; pub mod files; pub mod path; pub mod register; -pub mod reset_pubkey; pub mod search; pub mod versioning; // Utilities / helpers mod utils; + +pub fn default_endpoints() -> Vec { + vec![ + versioning::version_endpoint(), + versioning::all_versions_endpoint(), + path::path_endpoint(), + search::search_endpoint(), + files::upload_endpoint(), + register::register_endpoint(), + register::confirm_email_endpoint(), + add_pubkey::request_email_add_pubkey(), + add_pubkey::confirm_add_pubkey(), + #[cfg(feature = "html")] + bookmark::bookmark_endpoint(), + ] +} diff --git a/lib/src/plugins/register.rs b/lib/src/plugins/register.rs index 5f5efea60..fc6ca3cfb 100644 --- a/lib/src/plugins/register.rs +++ b/lib/src/plugins/register.rs @@ -105,6 +105,7 @@ pub fn handle_register_name_and_email( .get_server_url() .clone() .set_path(urls::PATH_CONFIRM_EMAIL) + // .set_subdomain(Some(&name.to_string()))? .url(); confirm_url.set_query(Some(&format!("token={}", token))); let message = MailMessage { diff --git a/lib/src/populate.rs b/lib/src/populate.rs index d5d849c12..2c69dda24 100644 --- a/lib/src/populate.rs +++ b/lib/src/populate.rs @@ -247,7 +247,7 @@ pub fn populate_collections(store: &impl Storelike) -> AtomicResult<()> { pub fn populate_endpoints(store: &crate::Db) -> AtomicResult<()> { use crate::atomic_url::Routes; - let endpoints = crate::endpoints::default_endpoints(); + let endpoints = crate::plugins::default_endpoints(); let endpoints_collection = store.get_server_url().set_route(Routes::Endpoints); for endpoint in endpoints { let mut resource = endpoint.to_resource(store)?;