sitter 0.1.6

A library for managing user registration, login, and the user's things.
Documentation
use std::time::Instant;

use anyhow::{anyhow, Result};
use argon2::{self, Config, ThreadMode, Variant, Version};
use log::info;
use rand::Rng;
use uuid::Uuid;

use crate::person::{Person, PersonHook, PersonRequest};

pub struct Password;
impl Password {
    // Load secret salt from environment.
    fn random_salt() -> [u8; 24] {
        rand::thread_rng().gen::<[u8; 24]>()
    }
}

impl PersonHook for Password {
    /// Simple password validation function, requires passwords be at
    /// least 8 characters in length.
    fn validate(&self, person: &PersonRequest, action: &str) -> Result<()> {
        // @TODO: validate during update when supported.
        if action == "create" && person.pass.len() < 8 {
            return Err(anyhow!(
                "password too short, must be at least 8 characters long"
            ));
        }
        Ok(())
    }

    fn prepare(&self, person_request: &mut PersonRequest, _action: &str) -> Result<()> {
        let plain_text = person_request.pass.clone().into_bytes();
        let secret_salt = Password::random_salt();

        // @TODO: expose the configuration.
        let config = Config {
            // Argon2id is a hybrid of Argon2i and Argon2d, using a combination
            // of data-depending and data-independent memory accesses, which
            // gives some of Argon2i's resistance to side-channel cache timing
            // attacks and much of Argon2d's resistance to GPU cracking attacks.
            variant: Variant::Argon2id,
            // Version13 is the latest algorithm version.
            version: Version::Version13,
            // Per-lane memory allocation in KB.
            mem_cost: 262144,
            // How many iterations to make.
            time_cost: 8,
            // How many parallel lanes of memory to fill.
            lanes: 4,
            // Allow parallel processing.
            thread_mode: ThreadMode::Parallel,
            secret: &[],
            ad: &[],
            hash_length: 32,
        };

        // Track how long password hashing takes, to tune properly.
        let now = Instant::now();

        // Hash the password.
        person_request.pass = argon2::hash_encoded(&plain_text, &secret_salt, &config)?;

        // Log how long the hashing process took.
        info!("argon2 hashing took {:?}", now.elapsed().as_micros());

        Ok(())
    }

    /// No prepring of the id is done for passwords.
    fn prepare_id(&self, _id: &mut Uuid, _action: &str) -> Result<()> {
        Ok(())
    }

    /// No postprocessing is done of passwords.
    fn processed(&self, _person: &mut Person, _action: &str) -> Result<()> {
        Ok(())
    }
}