Lots of changes for this to happen. Passwords are now to be stored separately from the players in a separate table in the database, and will only be queried when necessary. Random salts are generated, and a hash of the salted password is stored along with the salt. Some functions and structs to do all this were added. If I did it right, this should make the password storage secure, even if nothing else is.master
parent
b38d96dd5c
commit
64114b6d68
@ -0,0 +1,55 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use rusqlite::params;
|
||||
|
||||
use crate::database::Db;
|
||||
use crate::id::Id;
|
||||
use crate::password::Password;
|
||||
use crate::result::RudeResult;
|
||||
use crate::try_log;
|
||||
|
||||
impl Db {
|
||||
/// Get a password
|
||||
pub fn get_password(&self, id: Id) -> RudeResult<Option<Password>> {
|
||||
let mut statement = try_log!(
|
||||
self.0
|
||||
.prepare("select salt, hash from passwords where player = ?"),
|
||||
"Unable to prepare sql statement"
|
||||
);
|
||||
|
||||
let mut rows = try_log!(statement.query(params![id]), "Unable to perform query");
|
||||
|
||||
if let Some(row) = try_log!(rows.next(), "Unable to retrieve row") {
|
||||
Ok(Some(try_log!(
|
||||
Password::try_from(row),
|
||||
"Unable to get Password from Row"
|
||||
)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Save a password
|
||||
pub fn save_password(&self, id: Id, password: &Password) -> RudeResult<()> {
|
||||
let mut statement = try_log!(
|
||||
self.0.prepare(
|
||||
"insert into passwords (player, salt, hash) values (?, ?, ?) on conflict(player) do update set player=?, salt=?, hash=?;"
|
||||
),
|
||||
"Unable to prepare statement"
|
||||
);
|
||||
|
||||
let _ = try_log!(
|
||||
statement.execute(params![
|
||||
id,
|
||||
password.salt,
|
||||
password.hash,
|
||||
id,
|
||||
password.salt,
|
||||
password.hash,
|
||||
]),
|
||||
"Unable to perform query"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
//! Player information
|
||||
|
||||
use std::cmp::PartialEq;
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
|
||||
use rusqlite::Row;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::hash::{hash, salt};
|
||||
|
||||
/// Containing object for the password
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Password {
|
||||
/// Salt
|
||||
pub salt: String,
|
||||
|
||||
/// Salted and hashed password
|
||||
pub hash: String,
|
||||
}
|
||||
|
||||
impl Password {
|
||||
/// Create a new password object from a string password.
|
||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||
let salt = salt();
|
||||
let hash = hash(s, &salt);
|
||||
|
||||
Self { salt, hash }
|
||||
}
|
||||
|
||||
/// Check the password against the provided password.
|
||||
pub fn check<S: Into<String>>(&self, s: S) -> bool {
|
||||
let s = s.into();
|
||||
let hash = hash(s, &self.salt);
|
||||
|
||||
self.hash == hash
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&Row<'a>> for Password {
|
||||
type Error = Box<dyn Error>;
|
||||
|
||||
fn try_from(row: &Row) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
salt: try_log!(row.get("salt"), "salt"),
|
||||
hash: try_log!(row.get("hash"), "hash"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Password {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.hash == other.hash
|
||||
}
|
||||
}
|
Loading…
Reference in new issue