Add the add_authenticated_route method
I'm really proud of how small the user_management example has gotten
This commit is contained in:
parent
502e68f1aa
commit
7854a2a4c4
|
@ -7,7 +7,7 @@ use northstar::{
|
||||||
Response,
|
Response,
|
||||||
Server,
|
Server,
|
||||||
user_management::{
|
user_management::{
|
||||||
User,
|
user::RegisteredUser,
|
||||||
UserManagementRoutes,
|
UserManagementRoutes,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -29,8 +29,8 @@ async fn main() -> Result<()> {
|
||||||
Server::bind(("0.0.0.0", GEMINI_PORT))
|
Server::bind(("0.0.0.0", GEMINI_PORT))
|
||||||
|
|
||||||
// Add our main routes
|
// Add our main routes
|
||||||
.add_route("/", handle_main)
|
.add_authenticated_route("/", handle_main)
|
||||||
.add_route("/update", handle_update)
|
.add_authenticated_route("/update", handle_update)
|
||||||
|
|
||||||
// Add routes for handling user authentication
|
// Add routes for handling user authentication
|
||||||
.add_um_routes::<String>("/")
|
.add_um_routes::<String>("/")
|
||||||
|
@ -46,74 +46,46 @@ async fn main() -> Result<()> {
|
||||||
/// haven't. Includes links to update your string (`/update`) or your account
|
/// haven't. Includes links to update your string (`/update`) or your account
|
||||||
/// (`/account`). Even though we haven't added an explicit handler for `/account`, this
|
/// (`/account`). Even though we haven't added an explicit handler for `/account`, this
|
||||||
/// route is managed by northstar.
|
/// route is managed by northstar.
|
||||||
async fn handle_main(request: Request) -> Result<Response> {
|
///
|
||||||
|
/// Because this route is registered as an authenticated route, any connections without a
|
||||||
// Check to see if the user is signed in. If they are, we get a copy of their data
|
/// certificate will be prompted to add a certificate and register.
|
||||||
// (just a simple [`String`] for this demo, but this can be any serializable struct.
|
async fn handle_main(_req: Request, user: RegisteredUser<String>) -> Result<Response> {
|
||||||
if let User::SignedIn(user) = request.user::<String>()? {
|
// If the user is signed in, render and return their page
|
||||||
|
let response = Document::new()
|
||||||
// If the user is signed in, render and return their page
|
.add_text("Your personal secret string:")
|
||||||
let response = Document::new()
|
.add_text(user.as_ref())
|
||||||
.add_text("Your personal secret string:")
|
.add_blank_line()
|
||||||
.add_text(user.as_ref())
|
.add_link("/update", "Change your string")
|
||||||
.add_blank_line()
|
.add_link("/account", "Update your account")
|
||||||
.add_link("/update", "Change your string")
|
.into();
|
||||||
.add_link("/account", "Update your account")
|
Ok(response)
|
||||||
.into();
|
|
||||||
Ok(response)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// If the user is not logged in, prompt them to go to /account
|
|
||||||
let response = Document::new()
|
|
||||||
.add_text("Please login to use this app.")
|
|
||||||
.add_blank_line()
|
|
||||||
.add_link("/account", "Login or create account")
|
|
||||||
.into();
|
|
||||||
Ok(response)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The update endpoint
|
/// The update endpoint
|
||||||
///
|
///
|
||||||
/// Users can update their secret string here. Users who haven't logged in will be
|
/// Users can update their secret string here.
|
||||||
/// promped to do so.
|
async fn handle_update(request: Request, mut user: RegisteredUser<String>) -> Result<Response> {
|
||||||
async fn handle_update(request: Request) -> Result<Response> {
|
|
||||||
|
|
||||||
// Check if the user is signed in again
|
// If the user is logged in, check to see if they provided any input. If they
|
||||||
if let User::SignedIn(mut user) = request.user::<String>()? {
|
// have, we can set that input as their new string, otherwise we ask them for it
|
||||||
|
if let Some(string) = request.input() {
|
||||||
|
|
||||||
// If the user is logged in, check to see if they provided any input. If they
|
// Update the users data
|
||||||
// have, we can set that input as their new string, otherwise we ask them for it
|
*user.as_mut() = string.to_owned();
|
||||||
if let Some(string) = request.input() {
|
|
||||||
|
|
||||||
// Update the users data
|
// Render a response
|
||||||
*user.as_mut() = string.to_owned();
|
|
||||||
|
|
||||||
// Render a response
|
|
||||||
let response = Document::new()
|
|
||||||
.add_text("String updated!")
|
|
||||||
.add_blank_line()
|
|
||||||
.add_link("/", "Back")
|
|
||||||
.into();
|
|
||||||
Ok(response)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Ask the user for some input
|
|
||||||
Ok(Response::input_lossy("Enter your new string"))
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// The user isn't logged in, so we should ask them too
|
|
||||||
let response = Document::new()
|
let response = Document::new()
|
||||||
.add_text("Please login to use this app.")
|
.add_text("String updated!")
|
||||||
.add_blank_line()
|
.add_blank_line()
|
||||||
.add_link("/account", "Login or create account")
|
.add_link("/", "Back")
|
||||||
.into();
|
.into();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Ask the user for some input
|
||||||
|
Ok(Response::input_lossy("Enter your new string"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ use anyhow::Result;
|
||||||
use tokio::net::ToSocketAddrs;
|
use tokio::net::ToSocketAddrs;
|
||||||
use serde::{Serialize, de::DeserializeOwned};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::{Document, Request, Response};
|
use crate::{Document, Request, Response};
|
||||||
use crate::types::document::HeadingLevel;
|
use crate::types::document::HeadingLevel;
|
||||||
use crate::user_management::{User, RegisteredUser, UserManagerError};
|
use crate::user_management::{User, RegisteredUser, UserManagerError};
|
||||||
|
@ -27,12 +29,30 @@ pub trait UserManagementRoutes: private::Sealed {
|
||||||
/// The `redir` argument allows you to specify the point that users will be directed
|
/// The `redir` argument allows you to specify the point that users will be directed
|
||||||
/// to return to once their account has been created.
|
/// to return to once their account has been created.
|
||||||
fn add_um_routes<UserData: Serialize + DeserializeOwned + Default + 'static>(self, redir: &'static str) -> Self;
|
fn add_um_routes<UserData: Serialize + DeserializeOwned + Default + 'static>(self, redir: &'static str) -> Self;
|
||||||
|
|
||||||
|
/// Add a special route that requires users to be logged in
|
||||||
|
///
|
||||||
|
/// In addition to the normal [`Request`], your handler will recieve a copy of the
|
||||||
|
/// [`RegisteredUser`] for the current user. If a user tries to connect to the page
|
||||||
|
/// without logging in, they will be prompted to register or link an account.
|
||||||
|
///
|
||||||
|
/// To use this method, ensure that [`add_um_routes()`](Self::add_um_routes()) has
|
||||||
|
/// also been called.
|
||||||
|
fn add_authenticated_route<UserData, Handler, F>(
|
||||||
|
self,
|
||||||
|
path: &'static str,
|
||||||
|
handler: Handler,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
UserData: Serialize + DeserializeOwned + 'static + Send + Sync,
|
||||||
|
Handler: Send + Sync + 'static + Fn(Request, RegisteredUser<UserData>) -> F,
|
||||||
|
F: Send + Sync + 'static + Future<Output = Result<Response>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: ToSocketAddrs> UserManagementRoutes for crate::Builder<A> {
|
impl<A: ToSocketAddrs> UserManagementRoutes for crate::Builder<A> {
|
||||||
/// Add pre-configured routes to the serve to handle authentication
|
/// Add pre-configured routes to the serve to handle authentication
|
||||||
///
|
///
|
||||||
/// See [`UserManagementRoutes`]
|
/// See [`UserManagementRoutes::add_um_routes()`]
|
||||||
fn add_um_routes<UserData: Serialize + DeserializeOwned + Default + 'static>(self, redir: &'static str) -> Self {
|
fn add_um_routes<UserData: Serialize + DeserializeOwned + Default + 'static>(self, redir: &'static str) -> Self {
|
||||||
self.add_route("/account", move|r|handle_base::<UserData>(r, redir))
|
self.add_route("/account", move|r|handle_base::<UserData>(r, redir))
|
||||||
.add_route("/account/askcert", move|r|handle_ask_cert::<UserData>(r, redir))
|
.add_route("/account/askcert", move|r|handle_ask_cert::<UserData>(r, redir))
|
||||||
|
@ -40,6 +60,38 @@ impl<A: ToSocketAddrs> UserManagementRoutes for crate::Builder<A> {
|
||||||
.add_route("/account/login", move|r|handle_login::<UserData>(r, redir))
|
.add_route("/account/login", move|r|handle_login::<UserData>(r, redir))
|
||||||
.add_route("/account/password", handle_password::<UserData>)
|
.add_route("/account/password", handle_password::<UserData>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a special route that requires users to be logged in
|
||||||
|
///
|
||||||
|
/// See [`UserManagementRoutes::add_authenticated_route()`]
|
||||||
|
fn add_authenticated_route<UserData, Handler, F>(
|
||||||
|
self,
|
||||||
|
path: &'static str,
|
||||||
|
handler: Handler,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
UserData: Serialize + DeserializeOwned + 'static + Send + Sync,
|
||||||
|
Handler: Send + Sync + 'static + Fn(Request, RegisteredUser<UserData>) -> F,
|
||||||
|
F: Send + Sync + 'static + Future<Output = Result<Response>>
|
||||||
|
{
|
||||||
|
let handler = std::sync::Arc::new(handler);
|
||||||
|
self.add_route(path, move|request| {
|
||||||
|
let handler = handler.clone();
|
||||||
|
async move {
|
||||||
|
Ok(match request.user::<UserData>()? {
|
||||||
|
User::Unauthenticated => {
|
||||||
|
Response::success_gemini(UNAUTH)
|
||||||
|
},
|
||||||
|
User::NotSignedIn(_) => {
|
||||||
|
Response::success_gemini(NSI)
|
||||||
|
},
|
||||||
|
User::SignedIn(user) => {
|
||||||
|
(handler)(request, user).await?
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_base<UserData: Serialize + DeserializeOwned>(request: Request, redirect: &'static str) -> Result<Response> {
|
async fn handle_base<UserData: Serialize + DeserializeOwned>(request: Request, redirect: &'static str) -> Result<Response> {
|
||||||
|
|
Loading…
Reference in a new issue