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