Skip to content

Commit

Permalink
rwf-auth migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
levkk committed Dec 9, 2024
1 parent ee4eb92 commit 7386258
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 48 deletions.
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.

1 change: 1 addition & 0 deletions examples/users/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2021"
rwf = { path = "../../rwf" }
time = "0.3"
argon2 = "0.5"
rwf-auth = { path = "../../rwf-auth" }
1 change: 1 addition & 0 deletions examples/users/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod models;
#[tokio::main]
async fn main() {
Logger::init();
rwf_auth::migrate().await.expect("rwf-auth migrations");

let signup: SignupController<models::User> =
SignupController::new("templates/signup.html").redirect("/profile");
Expand Down
1 change: 1 addition & 0 deletions rwf-auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "rwf-auth"
version = "0.1.0"
edition = "2021"
include = ["migrations/", "src/", "static/", "templates/"]

[dependencies]
rwf = { path = "../rwf", version = "0.2.1" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE rwf_auth_users;
8 changes: 8 additions & 0 deletions rwf-auth/migrations/1733765331409957000_rwf_auth_users.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE rwf_auth_users (
id BIGSERIAL PRIMARY KEY,
identifier VARCHAR NOT NULL UNIQUE,
password VARCHAR NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW ()
);

CREATE INDEX ON rwf_auth_users USING btree (created_at);
4 changes: 3 additions & 1 deletion rwf-auth/src/controllers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This file is automatically generated by rwf-cli.
// Manual modifications to this file will not be preserved.
pub mod password;
pub mod password;

pub use password::{Password, PasswordController};
52 changes: 46 additions & 6 deletions rwf-auth/src/controllers/password.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,48 @@
//! Password authentication controller.
//!
//! Create an account if one doesn't exist. If one exists, attempt to log in.
//!
//! ### Errors
//!
//! The following errors are set in the template:
//!
//! - `error_form`: Any of the required fields are missing.
//! - `error_password`: Account exists and the password is incorrect.
//! - `error_password2`: Passwords do not match on account creation. Only set if `<input name="password2">` is present in the form.
use std::marker::PhantomData;

use rwf::{
model::{user::Error as UserError, UserModel},
prelude::*,
};

use crate::models::User;

/// Account creation and login form.
#[derive(macros::Form)]
struct PasswordForm {
pub struct PasswordForm {
identifier: String,
password: String,
password2: Option<String>,
}

/// Errors passed to the template.
/// Password errors.
///
/// These are passed to the template in the context.
#[derive(macros::Context, Default)]
pub struct Errors {
/// Something was wrong with the identifier.
pub error_identifier: bool,
/// Form has missing fields.
pub error_form: bool,
/// Password was incorrect or the user didn't exist.
pub error_password: bool,
/// Passwords do not match.
pub error_password2: bool,
}

impl Errors {
fn form() -> Self {
let mut ctx = Self::default();
ctx.error_identifier = true;
ctx.error_password = true;
ctx.error_form = true;
ctx
}

Expand All @@ -33,8 +51,16 @@ impl Errors {
ctx.error_password = true;
ctx
}

fn wrong_password_match() -> Self {
let mut ctx = Self::default();
ctx.error_password2 = true;
ctx
}
}

/// Generic password authentication controller. Can be used with any model
/// which implements the [`rwf::model::UserModel`] trait.
#[derive(Default)]
pub struct Password<T: UserModel> {
template_path: String,
Expand All @@ -43,6 +69,7 @@ pub struct Password<T: UserModel> {
}

impl<T: UserModel> Password<T> {
/// Create controller with the specified template path.
pub fn template(template_path: &str) -> Self {
Self {
template_path: template_path.to_owned(),
Expand All @@ -51,6 +78,7 @@ impl<T: UserModel> Password<T> {
}
}

/// Redirect to the specified URL on successful authentication.
pub fn redirect(mut self, redirect_url: &str) -> Self {
self.redirect_url = redirect_url.to_owned();
self
Expand Down Expand Up @@ -79,6 +107,15 @@ impl<T: UserModel> PageController for Password<T> {
return Ok(Response::new().html(tpl.render(Errors::form())?).code(400));
};

// If second password passed in, make sure they match.
if let Some(ref password2) = form.password2 {
if password2 != &form.password {
return Ok(Response::new()
.html(tpl.render(Errors::wrong_password_match())?)
.code(400));
}
}

let user = match T::create_user(&form.identifier, &form.password).await {
Ok(user) => user,
Err(UserError::UserExists) => {
Expand All @@ -99,3 +136,6 @@ impl<T: UserModel> PageController for Password<T> {
Ok(request.login_user(&user)?.redirect(&self.redirect_url))
}
}

/// Password controller implemented for the [`rwf_auth::models::User`] model.
pub type PasswordController = Password<User>;
10 changes: 10 additions & 0 deletions rwf-auth/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
use std::path::PathBuf;

use rwf::model::{Error, Migrations};

pub mod controllers;
pub mod models;

/// Run `rwf-auth` migrations.
pub async fn migrate() -> Result<Migrations, Error> {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
rwf::model::migrate(Some(path)).await
}
10 changes: 10 additions & 0 deletions rwf-auth/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use rwf::prelude::*;

#[derive(Clone, macros::Model, macros::UserModel, Debug)]
#[table_name("rwf_auth_users")]
pub struct User {
id: Option<i64>,
identifier: String,
password: String,
created_at: OffsetDateTime,
}
1 change: 1 addition & 0 deletions rwf-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ readme = "README.md"
[dependencies]
clap = { version = "4.5.18", features = ["derive"] }
rwf = { path = "../rwf", version = "0.2" }
rwf-auth = { path = "../rwf-auth", version = "0.1.0" }
tokio = { version = "1", features = ["full"] }
log = "0.4"
time = "0.3"
Expand Down
18 changes: 15 additions & 3 deletions rwf-cli/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,20 @@ use time::OffsetDateTime;
use regex::Regex;
use tokio::fs::{create_dir, File};

use crate::logging::created;
use crate::{logging::created, util::package_info};

pub async fn migrate(version: Option<i64>) {
let migrations = Migrations::sync().await.expect("failed to sync migrations");
let info = package_info().await.expect("couldn't get package info");

if info.rwf_auth {
rwf_auth::migrate()
.await
.expect("rwf-auth migrations failed to apply");
}

let migrations = Migrations::sync(None)
.await
.expect("failed to sync migrations");

migrations
.apply(Direction::Up, version)
Expand All @@ -17,7 +27,9 @@ pub async fn migrate(version: Option<i64>) {
}

pub async fn revert(version: Option<i64>) {
let migrations = Migrations::sync().await.expect("failed to sync migrations");
let migrations = Migrations::sync(None)
.await
.expect("failed to sync migrations");
let version = if let Some(version) = version {
Some(version)
} else {
Expand Down
9 changes: 0 additions & 9 deletions rwf-cli/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,6 @@ pub async fn setup() {
}

// Add rwf dependencies
Command::new("cargo")
.arg("add")
.arg("tokio@1")
.arg("--features")
.arg("full")
.status()
.await
.unwrap();

Command::new("cargo")
.arg("add")
.arg("rwf")
Expand Down
12 changes: 12 additions & 0 deletions rwf-cli/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct PackageInfo {
#[allow(dead_code)]
pub version: String,
pub target_dir: String,
pub rwf_auth: bool,
}

async fn cargo_toml() -> Result<Value, Box<dyn std::error::Error + 'static>> {
Expand All @@ -32,6 +33,16 @@ pub async fn package_info() -> Result<PackageInfo, Box<dyn std::error::Error + '
.get("version")
.expect("Cargo.toml to have a valid \"name\" field");

let rwf_auth = toml
.get("dependencies")
.expect("Cargo.toml to have a valid [dependencies] attribute");

let rwf_auth = rwf_auth
.as_table()
.expect("[dependencies] to be a table")
.iter()
.any(|dep| dep.0 == "rwf-auth");

let metadata = Command::new("cargo").arg("metadata").output().await?.stdout;
let json: serde_json::Value = serde_json::from_slice(&metadata)?;
let target_dir = json["target_directory"].as_str().unwrap().to_string();
Expand All @@ -40,5 +51,6 @@ pub async fn package_info() -> Result<PackageInfo, Box<dyn std::error::Error + '
name: name.as_str().unwrap().to_string(),
version: version.as_str().unwrap().to_string(),
target_dir,
rwf_auth,
})
}
28 changes: 14 additions & 14 deletions rwf-macros/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ pub(crate) fn impl_derive_user_model(input: TokenStream) -> TokenStream {
if let Some(attr) = input.attrs.first() {
match attr.meta {
Meta::List(ref attrs) => {
let attrs = syn::parse2::<UserModel>(attrs.tokens.clone()).unwrap();

let identifier = attrs.identifier.to_string();
let password = attrs.password.to_string();

return quote! {
impl rwf::model::UserModel for #ident {
fn identifier_column() -> &'static str {
#identifier
}

fn password_column() -> &'static str {
#password
if let Ok(attrs) = syn::parse2::<UserModel>(attrs.tokens.clone()) {
let identifier = attrs.identifier.to_string();
let password = attrs.password.to_string();

return quote! {
impl rwf::model::UserModel for #ident {
fn identifier_column() -> &'static str {
#identifier
}

fn password_column() -> &'static str {
#password
}
}
}
.into();
}
.into();
}

_ => (),
Expand Down
2 changes: 1 addition & 1 deletion rwf-tests/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.init();

rollback().await?;
migrate().await?;
migrate(None).await?;

let pool = Pool::from_env();
let mut conn = pool.get().await?;
Expand Down
Loading

0 comments on commit 7386258

Please sign in to comment.