Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ jobs:
message: ${{ steps.extract-version.outputs.VERSION }}
token: ${{ steps.app-token.outputs.token }}

- name: Reset and pull
run: git reset --hard && git pull

- name: Tag
uses: bruno-fs/repo-tagger@1.0.0
with:
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions examples/dioxus-axum/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async fn main() {
};
use shield::{ErasedMethod, Method, Shield, ShieldOptions};
use shield_bootstrap::BootstrapDioxusStyle;
use shield_dioxus_axum::{AxumDioxusIntegration, ShieldLayer};
use shield_dioxus_axum::{AuthRoutes, AxumDioxusIntegration, ShieldLayer};
use shield_memory::{MemoryStorage, User};
use shield_oidc::{Keycloak, OidcMethod};
use tokio::net::TcpListener;
Expand All @@ -45,7 +45,7 @@ async fn main() {
let storage = MemoryStorage::new();
let shield = Shield::new(
storage.clone(),
vec![
vec![Arc::new(
OidcMethod::new(storage).with_providers([Keycloak::builder(
"keycloak",
"http://localhost:18080/realms/Shield",
Expand All @@ -59,13 +59,14 @@ async fn main() {
.unwrap_or_else(|| addr.port())
))
.build()]),
],
)],
ShieldOptions::default(),
);
let shield_layer = ShieldLayer::new(shield.clone());

// Initialize router
let router = Router::new()
.nest("/api/auth", AuthRoutes::router::<User, ()>())
.serve_dioxus_application(
ServeConfig::builder()
.context(AxumDioxusIntegration::<User>::default().context())
Expand Down
6 changes: 3 additions & 3 deletions examples/leptos-axum/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use leptos::prelude::*;
use leptos_meta::{MetaTags, Title, provide_meta_context};
use leptos_router::{
components::{Outlet, ParentRoute, Router, Routes},
components::{Outlet, ParentRoute, Route, Router, Routes},
path,
};
use shield_leptos::ShieldRouter;

// use crate::home::HomePage;
use crate::home::HomePage;

pub fn shell(options: LeptosOptions) -> impl IntoView {
view! {
Expand Down Expand Up @@ -44,7 +44,7 @@ pub fn App() -> impl IntoView {
<Router>
<main>
<Routes fallback=|| "Not found.".into_view()>
// <Route path=path!("") view=HomePage />
<Route path=path!("") view=HomePage />

<ParentRoute path=path!("auth") view=Outlet>
<ShieldRouter />
Expand Down
49 changes: 26 additions & 23 deletions examples/leptos-axum/src/home.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use leptos::{either::Either, prelude::*};
use leptos_router::components::A;
use leptos::{
// either::Either,
prelude::*,
};
// use leptos_router::components::A;
use shield_leptos::LeptosUser;

#[server]
Expand All @@ -11,31 +14,31 @@ pub async fn user() -> Result<Option<LeptosUser>, ServerFnError> {

#[component]
pub fn HomePage() -> impl IntoView {
let user = OnceResource::new(user());
// let user = OnceResource::new(user());

view! {
<h1>"Shield Leptos Axum Example"</h1>

<Suspense fallback=|| view! { "Loading..." }>
{move || Suspend::new(async move { match user.await {
Ok(user) => Either::Left(match user {
Some(user) => Either::Left(view! {
<p><b>User ID:</b> {user.id}</p>
// <Suspense fallback=|| view! { "Loading..." }>
// {move || Suspend::new(async move { match user.await {
// Ok(user) => Either::Left(match user {
// Some(user) => Either::Left(view! {
// <p><b>User ID:</b> {user.id}</p>

<A href="/auth/sign-out">
<button>"Sign out"</button>
</A>
}),
None => Either::Right(view! {
<A href="/auth/sign-in">
<button>"Sign in"</button>
</A>
}),
}),
Err(err) => Either::Right(view! {
{err.to_string()}
})
}})}
</Suspense>
// <A href="/auth/sign-out">
// <button>"Sign out"</button>
// </A>
// }),
// None => Either::Right(view! {
// <A href="/auth/sign-in">
// <button>"Sign in"</button>
// </A>
// }),
// }),
// Err(err) => Either::Right(view! {
// {err.to_string()}
// })
// }})}
// </Suspense>
}
}
55 changes: 42 additions & 13 deletions packages/core/shield/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use crate::{
error::ShieldError, form::Form, provider::Provider, request::Request, response::Response,
session::Session,
error::ShieldError,
form::Form,
provider::Provider,
request::Request,
response::Response,
session::{BaseSession, MethodSession},
};

// TODO: Think of a better name.
Expand All @@ -31,12 +35,12 @@ pub struct ActionProviderForm {
}

#[async_trait]
pub trait Action<P: Provider>: ErasedAction + Send + Sync {
pub trait Action<P: Provider, S>: ErasedAction + Send + Sync {
fn id(&self) -> String;

fn name(&self) -> String;

fn condition(&self, _provider: &P, _session: Session) -> Result<bool, ShieldError> {
fn condition(&self, _provider: &P, _session: &MethodSession<S>) -> Result<bool, ShieldError> {
Ok(true)
}

Expand All @@ -45,7 +49,7 @@ pub trait Action<P: Provider>: ErasedAction + Send + Sync {
async fn call(
&self,
provider: P,
session: Session,
session: &MethodSession<S>,
request: Request,
) -> Result<Response, ShieldError>;
}
Expand All @@ -59,7 +63,8 @@ pub trait ErasedAction: Send + Sync {
fn erased_condition(
&self,
provider: &(dyn Any + Send + Sync),
session: Session,
base_session: &BaseSession,
method_session: &(dyn Any + Send + Sync),
) -> Result<bool, ShieldError>;

async fn erased_forms(
Expand All @@ -70,7 +75,8 @@ pub trait ErasedAction: Send + Sync {
async fn erased_call(
&self,
provider: Box<dyn Any + Send + Sync>,
session: Session,
base_session: &BaseSession,
method_session: &(dyn Any + Send + Sync),
request: Request,
) -> Result<Response, ShieldError>;
}
Expand All @@ -88,21 +94,44 @@ macro_rules! erased_action {
self.name()
}

fn erased_condition(&self, provider: &(dyn std::any::Any + Send + Sync), session: $crate::Session) -> Result<bool, $crate::ShieldError> {
self.condition(provider.downcast_ref().expect("TODO"), session)
fn erased_condition(
&self,
provider: &(dyn std::any::Any + Send + Sync),
base_session: &$crate::BaseSession,
method_session: &(dyn std::any::Any + Send + Sync)
) -> Result<bool, $crate::ShieldError> {
self.condition(
provider.downcast_ref().expect("Provider should be downcast"),
&MethodSession {
base: base_session,
method: method_session.downcast_ref().expect("Session should be downcast"),
},
)
}

async fn erased_forms(&self, provider: Box<dyn std::any::Any + Send + Sync>) -> Result<Vec<$crate::Form>, $crate::ShieldError> {
self.forms(*provider.downcast().expect("TODO")).await
async fn erased_forms(
&self,
provider: Box<dyn std::any::Any + Send + Sync>
) -> Result<Vec<$crate::Form>, $crate::ShieldError> {
self.forms(*provider.downcast().expect("Provider should be downcast")).await
}

async fn erased_call(
&self,
provider: Box<dyn std::any::Any + Send + Sync>,
session: $crate::Session,
base_session: &$crate::BaseSession,
method_session: &(dyn std::any::Any + Send + Sync),
request: $crate::Request,
) -> Result<$crate::Response, $crate::ShieldError> {
self.call(*provider.downcast().expect("TODO"), session, request)
self
.call(
*provider.downcast().expect("Provider should be downcast"),
&$crate::MethodSession {
base: base_session,
method: method_session.downcast_ref().expect("Session should be downcast"),
},
request
)
.await
}
}
Expand Down
7 changes: 5 additions & 2 deletions packages/core/shield/src/actions/sign_in_callback.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Provider, Session, ShieldError};
use crate::{MethodSession, Provider, ShieldError};

const ACTION_ID: &str = "sign-in-callback";
const ACTION_NAME: &str = "Sign in callback";
Expand All @@ -14,7 +14,10 @@ impl SignInCallbackAction {
ACTION_NAME.to_owned()
}

pub fn condition<P: Provider>(_provider: &P, _session: Session) -> Result<bool, ShieldError> {
pub fn condition<P: Provider, S>(
_provider: &P,
_session: &MethodSession<S>,
) -> Result<bool, ShieldError> {
Ok(true)
}
}
17 changes: 7 additions & 10 deletions packages/core/shield/src/actions/sign_out.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::{
Form, Input, InputType, InputTypeSubmit, Provider, Session, SessionError, ShieldError,
};
use crate::{Form, Input, InputType, InputTypeSubmit, MethodSession, Provider, ShieldError};

const ACTION_ID: &str = "sign-out";
const ACTION_NAME: &str = "Sign out";
Expand All @@ -16,13 +14,12 @@ impl SignOutAction {
ACTION_NAME.to_owned()
}

pub fn condition<P: Provider>(provider: &P, session: Session) -> Result<bool, ShieldError> {
let session_data = session.data();
let session_data = session_data
.lock()
.map_err(|err| SessionError::Lock(err.to_string()))?;

Ok(session_data
pub fn condition<P: Provider, S>(
provider: &P,
session: &MethodSession<S>,
) -> Result<bool, ShieldError> {
Ok(session
.base
.authentication
.as_ref()
.is_some_and(|authentication| {
Expand Down
51 changes: 43 additions & 8 deletions packages/core/shield/src/method.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
use std::any::Any;

use async_trait::async_trait;
use serde::{Serialize, de::DeserializeOwned};

use crate::{ErasedAction, action::Action, error::ShieldError, provider::Provider};
use crate::{
ErasedAction,
action::Action,
error::{SessionError, ShieldError},
provider::Provider,
};

#[async_trait]
pub trait Method<P: Provider>: Send + Sync {
pub trait Method: Send + Sync {
type Provider: Provider;
type Session: DeserializeOwned + Serialize;

fn id(&self) -> String;

fn actions(&self) -> Vec<Box<dyn Action<P>>>;
fn actions(&self) -> Vec<Box<dyn Action<Self::Provider, Self::Session>>>;

fn action_by_id(&self, action_id: &str) -> Option<Box<dyn Action<P>>> {
fn action_by_id(
&self,
action_id: &str,
) -> Option<Box<dyn Action<Self::Provider, Self::Session>>> {
self.actions()
.into_iter()
.find(|action| action.id() == action_id)
}

async fn providers(&self) -> Result<Vec<P>, ShieldError>;
async fn providers(&self) -> Result<Vec<Self::Provider>, ShieldError>;

async fn provider_by_id(&self, provider_id: Option<&str>) -> Result<Option<P>, ShieldError> {
async fn provider_by_id(
&self,
provider_id: Option<&str>,
) -> Result<Option<Self::Provider>, ShieldError> {
Ok(self
.providers()
.await?
Expand All @@ -43,6 +58,11 @@ pub trait ErasedMethod: Send + Sync {
&self,
provider_id: Option<&str>,
) -> Result<Option<Box<dyn Any + Send + Sync>>, ShieldError>;

fn erased_deserialize_session(
&self,
value: Option<&str>,
) -> Result<Box<dyn Any + Send + Sync>, SessionError>;
}

#[macro_export]
Expand Down Expand Up @@ -71,7 +91,7 @@ macro_rules! erased_method {

async fn erased_providers(
&self,
) -> Result<Vec<(Option<String>, Box<dyn std::any::Any + Send + Sync>)>, ShieldError> {
) -> Result<Vec<(Option<String>, Box<dyn std::any::Any + Send + Sync>)>, $crate::ShieldError> {
self.providers().await.map(|providers| {
providers
.into_iter()
Expand All @@ -83,12 +103,27 @@ macro_rules! erased_method {
async fn erased_provider_by_id(
&self,
provider_id: Option<&str>,
) -> Result<Option<Box<dyn std::any::Any + Send + Sync>>, ShieldError> {
) -> Result<Option<Box<dyn std::any::Any + Send + Sync>>, $crate::ShieldError> {
self.provider_by_id(provider_id).await.map(|provider| {
provider
.map(|provider| Box::new(provider) as Box<dyn std::any::Any + Send + Sync>)
})
}

fn erased_deserialize_session(
&self,
value: Option<&str>
) -> Result<Box<dyn std::any::Any + Send + Sync>, $crate::SessionError> {
type Session $( < $( $generic_name ),+ > )* = <$method $( < $( $generic_name ),+ > )* as $crate::Method>::Session;

let session = match value {
Some(value) => serde_json::from_str::<Session $( < $( $generic_name ),+ > )* >(value)
.map_err(|err| $crate::SessionError::Serialization(err.to_string()))?,
None => Session $( ::< $( $generic_name ),+ > )* ::default()
};

Ok(Box::new(session) as Box<dyn std::any::Any + Send + Sync>)
}
}
};
}
Loading