From 952b2d22891d31a1a73362eb4a85707494b89caf Mon Sep 17 00:00:00 2001 From: "Andrew Wheeler(Genusis)" Date: Thu, 24 Feb 2022 14:48:42 -0500 Subject: [PATCH] Axumsessionauth (#3) * Changed API to support SqlxSessions multi supported Pool and Connection * Update readme to include new changes and Changed version to 0.2 * make sqlx use current test branch * Readme with new Changes from AxumSqlxSessions. Publish is waiting on https://github.com/launchbadge/sqlx/pull/1652 if this is not implemented then I will make a implementation lib that replaces their Any with my own version * Fixed missing Some() in readme * Updated SQLx to FromForAny * add any feature to sqlx * Added features with default postgres for sqlx * Rename with AxumSession usage instead of the sqlx version * Rename and a few changes --- Cargo.toml | 34 ++++++++++++----------- LICENSE | 2 +- README.md | 26 +++++++++--------- axumsqlxsessionauth.code-workspace | 8 ------ src/auth.rs | 8 +++--- src/layer.rs | 9 ++++--- src/lib.rs | 11 +++++++- src/session.rs | 43 +++++++++--------------------- 8 files changed, 67 insertions(+), 74 deletions(-) delete mode 100644 axumsqlxsessionauth.code-workspace diff --git a/Cargo.toml b/Cargo.toml index f042289..639f645 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,26 +1,30 @@ [package] -name = "axum_sqlx_session_auth" -version = "0.1.1" +name = "axum_sessions_auth" +version = "0.1.0" authors = ["Andrew Wheeler "] -description = "Library to Provide a User Authentication and privilege Token Checks. It requires the AxumSQLxSessions library and Tower_cookies library." +description = "Library to Provide a User Authentication and privilege Token Checks. It requires the Axum_Database_Sessions library and Tower_cookies library." edition = "2021" license = "MIT" readme = "README.md" -documentation = "https://docs.rs/axum_sqlx_session_auth" +documentation = "https://docs.rs/axum_sessions_auth" keywords = ["Axum", "Tower", "SQLx", "Session", "Authentication"] -repository = "https://github.com/AscendingCreations/axum_sqlx_session_auth" +repository = "https://github.com/AscendingCreations/AxumSessionsAuth" + +[features] +sqlite = ["sqlx/sqlite", "axum_database_sessions/sqlite"] +postgres = ["sqlx/postgres", "axum_database_sessions/postgres"] +mysql = ["sqlx/mysql", "axum_database_sessions/mysql"] +rustls = ["sqlx/runtime-tokio-rustls", "axum_database_sessions/rustls"] +native-tls = [ + "sqlx/runtime-tokio-native-tls", + "axum_database_sessions/native-tls", +] [dependencies] axum = "0.4" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.16", features = ["full"] } async-trait = "0.1" -axum_sqlx_sessions = "0.1" -sqlx = { version = "0.5", features = [ - "runtime-tokio-rustls", - "postgres", - "chrono", - "json", - "all-types", -] } +axum_database_sessions = { version = "0.2" } +sqlx = { version = "0.5", features = ["chrono", "json", "all-types"] } anyhow = "1.0" -async-recursion = "0.3" +async-recursion = "1.0" diff --git a/LICENSE b/LICENSE index 85b7f3b..c4c9d9e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 Ascending Creations +Copyright (c) 2022 Ascending Creations Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6cf7e22..af9b644 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ -# axum_sqlx_session_auth +# axum_sessions_auth -Library to Provide a User Authentication and privilege Token Checks. It requires the AxumSQLxSessions library and Tower_cookies library. +Library to Provide a User Authentication and privilege Token Checks. It requires the Axum_Database_Sessions library and Tower_cookies library. This library will help by making it so User ID or authorizations are not stored on the Client side but rather on the Server side. The Authorization is linked by the Clients Serverside Session ID which is stored on the Client side. -[![https://crates.io/crates/axum_sqlx_session_auth](https://img.shields.io/badge/crates.io-v0.1.1-blue)](https://crates.io/crates/axum_sqlx_session_auth) -[![Docs](https://docs.rs/axum_sqlx_session_auth/badge.svg)](https://docs.rs/axum_sqlx_session_auth) +You must choose only one of ['postgres', 'mysql', 'sqlite'] features to use this library. + +[![https://crates.io/crates/axum_sessions_auth](https://img.shields.io/badge/crates.io-v0.1.0-blue)](https://crates.io/crates/axum_sessions_auth) +[![Docs](https://docs.rs/axum_sessions_auth/badge.svg)](https://docs.rs/axum_sessions_auth) # Example ```rust use sqlx::{ConnectOptions, postgres::{PgPoolOptions, PgConnectOptions}}; use std::net::SocketAddr; -use axum_sqlx_sessions::{SqlxSession, SqlxSessionConfig, SqlxSessionLayer}; -use axum_sqlx_session_auth::{AuthSession, AuthSessionLayer, Authentication}; +use axum_database_sessions::{AxumSession, AxumSessionConfig, AxumSessionLayer, AxumDatabasePool}; +use axum_sessions_auth::{AuthSession, AuthSessionLayer, Authentication}; use axum::{ Router, routing::get, @@ -24,7 +26,7 @@ async fn main() { # async { let poll = connect_to_database().await.unwrap(); - let session_config = SqlxSessionConfig::default() + let session_config = AxumSessionConfig::default() .with_database("test") .with_table_name("test_table"); @@ -32,8 +34,8 @@ async fn main() { let app = Router::new() .route("/greet/:name", get(greet)) .layer(tower_cookies::CookieManagerLayer::new()) - .layer(SqlxSessionLayer::new(session_config, poll.clone())) - .layer(AuthSessionLayer::new(poll.clone(), Some(1))) + .layer(AxumSessionLayer::new(session_config, poll.clone().into())) + .layer(AuthSessionLayer::new(Some(poll.clone().into()), Some(1))) // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); @@ -47,7 +49,7 @@ async fn main() { //we can get the Method to compare with what Methods we allow. Useful if thius supports multiple methods. //When called auth is loaded in the background for you. -async fn greet(method: Method, session: SQLxSession, auth: AuthSession) -> &'static str { +async fn greet(method: Method, session: AxumSession, auth: AuthSession) -> &'static str { let mut count: usize = session.get("count").unwrap_or(0); count += 1; session.set("count", count); @@ -99,7 +101,7 @@ pub struct User { //This is only used if you want to use Token based Authentication checks #[async_trait] impl HasPermission for User { - async fn has(&self, perm: &String, pool: &Option<&mut PoolConnection>) -> bool { + async fn has(&self, perm: &String, pool: &Option<&AxumDatabasePool>) -> bool { match &perm[..] { "Token::UseAdmin" => true, "Token::ModifyUser" => true, @@ -110,7 +112,7 @@ impl HasPermission for User { #[async_trait] impl Authentication for User { - async fn load_user(userid: i64, _pool: Option<&mut PoolConnection>) -> Result { + async fn load_user(userid: i64, _pool: Option<&AxumDatabasePool>) -> Result { Ok(User { id: userid, anonymous: true, diff --git a/axumsqlxsessionauth.code-workspace b/axumsqlxsessionauth.code-workspace deleted file mode 100644 index 876a149..0000000 --- a/axumsqlxsessionauth.code-workspace +++ /dev/null @@ -1,8 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ], - "settings": {} -} \ No newline at end of file diff --git a/src/auth.rs b/src/auth.rs index 58b5ff9..200048a 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,13 +1,13 @@ use crate::Authentication; use async_recursion::async_recursion; use axum::{async_trait, http::Method}; -use sqlx::pool::PoolConnection; +use axum_database_sessions::AxumDatabasePool; use std::marker::PhantomData; ///Trait is used to check their Permissions via Tokens. uses a optional Database for SQL Token Checks too. #[async_trait] pub trait HasPermission { - async fn has(&self, perm: &str, pool: &Option<&mut PoolConnection>) -> bool; + async fn has(&self, perm: &str, pool: &Option<&AxumDatabasePool>) -> bool; } ///The Type of Rights a user needs will parse through these to check each point. @@ -41,7 +41,7 @@ impl Rights { pub async fn evaluate( &self, user: &(dyn HasPermission + Sync), - db: &Option<&mut PoolConnection>, + db: &Option<&AxumDatabasePool>, ) -> bool { match self { Self::All(rights) => { @@ -115,7 +115,7 @@ where &self, user: &D, method: &Method, - db: Option<&mut PoolConnection>, + db: Option<&AxumDatabasePool>, ) -> bool where D: HasPermission + Authentication, diff --git a/src/layer.rs b/src/layer.rs index 3393953..a79da5a 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,16 +1,19 @@ use axum::AddExtensionLayer; -use sqlx::postgres::PgPool; +use axum_database_sessions::AxumDatabasePool; /// Used to create and store the Extensions Data. #[derive(Clone, Debug)] pub struct AuthSessionLayer { - pub(crate) poll: Option, + pub(crate) poll: Option, pub(crate) anonymous_user_id: Option, } impl AuthSessionLayer { /// Creates a Extension so it can be accessed Directly within Requests. - pub fn new(poll: Option, anonymous_user_id: Option) -> AddExtensionLayer { + pub fn new( + poll: Option, + anonymous_user_id: Option, + ) -> AddExtensionLayer { AddExtensionLayer::new(Self { poll, anonymous_user_id, diff --git a/src/lib.rs b/src/lib.rs index 85859ec..3cee0f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,14 @@ #![doc = include_str!("../README.md")] -//Todo: Support more databases and expand the Tokio/RLS or RustRLS Selections for SQLx +#[cfg(not(any(feature = "postgres", feature = "mysql", feature = "sqlite",)))] +compile_error!("one of the features ['postgres', 'mysql', 'sqlite'] must be enabled"); + +#[cfg(any( + all(feature = "postgres", feature = "mysql"), + all(feature = "postgres", feature = "sqlite"), + all(feature = "mysql", feature = "sqlite"), +))] +compile_error!("only one of ['postgres', 'mysql', 'sqlite'] can be enabled"); + mod auth; ///This Library Requires that Tower_Cookies and AxumSQLxSessions is used as an active layer. mod layer; diff --git a/src/session.rs b/src/session.rs index a49e46d..e637e34 100644 --- a/src/session.rs +++ b/src/session.rs @@ -5,23 +5,19 @@ use axum::{ extract::{FromRequest, RequestParts}, http::{self, StatusCode}, }; -use axum_sqlx_sessions::SQLxSession; -use sqlx::pool::PoolConnection; +use axum_database_sessions::{AxumDatabasePool, AxumSession}; ///This is the AuthSession that is generated when a user is routed to a page that Needs one -/// It is used to load the user from his SQLxSession stored ID. +/// It is used to load the user from there SqlxSession stored ID. #[derive(Debug, Clone)] pub struct AuthSession { pub current_user: Option, - pub(crate) session: SQLxSession, + pub(crate) session: AxumSession, } #[async_trait] pub trait Authentication { - async fn load_user( - userid: i64, - pool: Option<&mut PoolConnection>, - ) -> Result; + async fn load_user(userid: i64, pool: Option<&AxumDatabasePool>) -> Result; fn is_authenticated(&self) -> bool; fn is_active(&self) -> bool; fn is_anonymous(&self) -> bool; @@ -43,7 +39,7 @@ where StatusCode::INTERNAL_SERVER_ERROR, "Can't extract SQLxSession: extensions has been taken by another extractor", ))?; - let session = extensions.get::().cloned().ok_or(( + let session = extensions.get::().cloned().ok_or(( StatusCode::INTERNAL_SERVER_ERROR, "Can't extract SQLxSession. Is `SQLxSessionLayer` enabled?", ))?; @@ -52,7 +48,7 @@ where "Can't extract AuthSessionLayer. Is `AuthSessionLayer` enabled?", ))?; - let current_id = if let Some(id) = session.get::("user_auth_session_id") { + let current_id = if let Some(id) = session.get::("user_auth_session_id").await { Some(id) } else { authlayer.anonymous_user_id @@ -63,22 +59,9 @@ where None => None, Some(uid) => { if let Some(poll) = &authlayer.poll { - let mut guard = poll.acquire().await.map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "Can't extract AuthSessionLayer. Is `AuthSessionLayer` enabled?", - ) - })?; - - match D::load_user(uid, Some(&mut guard)).await { - Ok(user) => Some(user), - Err(_) => None, - } + D::load_user(uid, Some(poll)).await.ok() } else { - match D::load_user(uid, None).await { - Ok(user) => Some(user), - Err(_) => None, - } + D::load_user(uid, None).await.ok() } } } @@ -120,16 +103,16 @@ where } /// Use this to Set the user id into the Session so it can auto login the user on request. - pub fn login_user(&self, id: i64) { - let value = self.session.get::("user_auth_session_id"); + pub async fn login_user(&self, id: i64) { + let value = self.session.get::("user_auth_session_id").await; if value != Some(id) { - self.session.set("user_auth_session_id", id); + self.session.set("user_auth_session_id", id).await; } } /// Use this to remove the users id from session. Forcing them to login as anonymous. - pub fn logout_user(&self) { - self.session.remove("user_auth_session_id"); + pub async fn logout_user(&self) { + self.session.remove("user_auth_session_id").await; } }