Skip to content

Commit

Permalink
Axumsessionauth (#3)
Browse files Browse the repository at this point in the history
* 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 launchbadge/sqlx#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
  • Loading branch information
genusistimelord committed Feb 24, 2022
1 parent 341348a commit 952b2d2
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 74 deletions.
34 changes: 19 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <genusistimelord@gmail.com>"]
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"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -24,16 +26,16 @@ 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");

// build our application with some routes
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));
Expand All @@ -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<User>) -> &'static str {
async fn greet(method: Method, session: AxumSession, auth: AuthSession<User>) -> &'static str {
let mut count: usize = session.get("count").unwrap_or(0);
count += 1;
session.set("count", count);
Expand Down Expand Up @@ -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<sqlx::Postgres>>) -> bool {
async fn has(&self, perm: &String, pool: &Option<&AxumDatabasePool>) -> bool {
match &perm[..] {
"Token::UseAdmin" => true,
"Token::ModifyUser" => true,
Expand All @@ -110,7 +112,7 @@ impl HasPermission for User {

#[async_trait]
impl Authentication<User> for User {
async fn load_user(userid: i64, _pool: Option<&mut PoolConnection<sqlx::Postgres>>) -> Result<User> {
async fn load_user(userid: i64, _pool: Option<&AxumDatabasePool>) -> Result<User> {
Ok(User {
id: userid,
anonymous: true,
Expand Down
8 changes: 0 additions & 8 deletions axumsqlxsessionauth.code-workspace

This file was deleted.

8 changes: 4 additions & 4 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -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<sqlx::Postgres>>) -> 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.
Expand Down Expand Up @@ -41,7 +41,7 @@ impl Rights {
pub async fn evaluate(
&self,
user: &(dyn HasPermission + Sync),
db: &Option<&mut PoolConnection<sqlx::Postgres>>,
db: &Option<&AxumDatabasePool>,
) -> bool {
match self {
Self::All(rights) => {
Expand Down Expand Up @@ -115,7 +115,7 @@ where
&self,
user: &D,
method: &Method,
db: Option<&mut PoolConnection<sqlx::Postgres>>,
db: Option<&AxumDatabasePool>,
) -> bool
where
D: HasPermission + Authentication<D>,
Expand Down
9 changes: 6 additions & 3 deletions src/layer.rs
Original file line number Diff line number Diff line change
@@ -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<PgPool>,
pub(crate) poll: Option<AxumDatabasePool>,
pub(crate) anonymous_user_id: Option<i64>,
}

impl AuthSessionLayer {
/// Creates a Extension so it can be accessed Directly within Requests.
pub fn new(poll: Option<PgPool>, anonymous_user_id: Option<i64>) -> AddExtensionLayer<Self> {
pub fn new(
poll: Option<AxumDatabasePool>,
anonymous_user_id: Option<i64>,
) -> AddExtensionLayer<Self> {
AddExtensionLayer::new(Self {
poll,
anonymous_user_id,
Expand Down
11 changes: 10 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
43 changes: 13 additions & 30 deletions src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<D> {
pub current_user: Option<D>,
pub(crate) session: SQLxSession,
pub(crate) session: AxumSession,
}

#[async_trait]
pub trait Authentication<D> {
async fn load_user(
userid: i64,
pool: Option<&mut PoolConnection<sqlx::Postgres>>,
) -> Result<D, Error>;
async fn load_user(userid: i64, pool: Option<&AxumDatabasePool>) -> Result<D, Error>;
fn is_authenticated(&self) -> bool;
fn is_active(&self) -> bool;
fn is_anonymous(&self) -> bool;
Expand All @@ -43,7 +39,7 @@ where
StatusCode::INTERNAL_SERVER_ERROR,
"Can't extract SQLxSession: extensions has been taken by another extractor",
))?;
let session = extensions.get::<SQLxSession>().cloned().ok_or((
let session = extensions.get::<AxumSession>().cloned().ok_or((
StatusCode::INTERNAL_SERVER_ERROR,
"Can't extract SQLxSession. Is `SQLxSessionLayer` enabled?",
))?;
Expand All @@ -52,7 +48,7 @@ where
"Can't extract AuthSessionLayer. Is `AuthSessionLayer` enabled?",
))?;

let current_id = if let Some(id) = session.get::<i64>("user_auth_session_id") {
let current_id = if let Some(id) = session.get::<i64>("user_auth_session_id").await {
Some(id)
} else {
authlayer.anonymous_user_id
Expand All @@ -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()
}
}
}
Expand Down Expand Up @@ -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::<i64>("user_auth_session_id");
pub async fn login_user(&self, id: i64) {
let value = self.session.get::<i64>("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;
}
}

0 comments on commit 952b2d2

Please sign in to comment.