commit 6ad8192fc9070977746041abc057f874fffab6f8
parent 71f5ea8885f845b5bb99263345c65e409e60f2e2
Author: Jackson G. Kaindume <kaindume@kwatafana.org>
Date: Thu, 22 Sep 2022 01:15:13 +0200
Update Readme
Diffstat:
13 files changed, 104 insertions(+), 59 deletions(-)
diff --git a/README.md b/README.md
@@ -1,6 +1,13 @@
<div style="text-align: right">
-<a href="https://codeberg.org/seestem/cyrtophora">
- <img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-white-on-black.png" height="60">
+Clone it on
+<a style="text-decoration: none" href="https://gitlab.com/kwatafana/cyrtophora">
+ <span style="color: #fc6d26">Gitlab</span>
+ <svg class="tanuki-logo" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path class="tanuki-shape tanuki" d="m24.507 9.5-.034-.09L21.082.562a.896.896 0 0 0-1.694.091l-2.29 7.01H7.825L5.535.653a.898.898 0 0 0-1.694-.09L.451 9.411.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 2.56 1.935 1.554 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z" fill="#E24329"></path>
+ <path class="tanuki-shape right-cheek" d="m24.507 9.5-.034-.09a11.44 11.44 0 0 0-4.56 2.051l-7.447 5.632 4.742 3.584 5.197-3.89.014-.01A6.297 6.297 0 0 0 24.507 9.5Z" fill="#FC6D26"></path>
+ <path class="tanuki-shape chin" d="m7.707 20.677 2.56 1.935 1.555 1.176a1.051 1.051 0 0 0 1.268 0l1.555-1.176 2.56-1.935-4.743-3.584-4.755 3.584Z" fill="#FCA326"></path>
+ <path class="tanuki-shape left-cheek" d="M5.01 11.461a11.43 11.43 0 0 0-4.56-2.05L.416 9.5a6.297 6.297 0 0 0 2.09 7.278l.012.01.03.022 5.16 3.867 4.745-3.584-7.444-5.632Z" fill="#FC6D26"></path>
+ </svg>
</a>
</div>
diff --git a/phora/src/account.rs b/phora/src/account.rs
@@ -1,4 +1,4 @@
-use crate::data::AccountRegistration;
+use crate::data::AccountCreationInput;
use crate::validate::ValidationError;
use serde::{Deserialize, Serialize};
@@ -15,19 +15,16 @@ pub struct Account {
impl Account {
/// Create new account
- pub fn create(payload: AccountRegistration) -> Result<PublicAccount, ValidationError> {
+ pub fn create(payload: AccountCreationInput) -> Result<Self, ValidationError> {
payload.is_valid()?;
- // TODO: email verification
- // TODO: database registration
-
let account = Account {
username: payload.username,
email: payload.email,
password: payload.password,
};
- Ok(account.into())
+ Ok(account)
}
}
diff --git a/phora/src/api.rs b/phora/src/api.rs
@@ -1,13 +0,0 @@
-pub struct API {
- pub version: String,
- pub accounts: String,
-}
-
-impl Default for API {
- fn default() -> Self {
- API {
- version: "/v0".to_string(),
- accounts: "/v0/accounts".to_string(),
- }
- }
-}
diff --git a/phora/src/data.rs b/phora/src/data.rs
@@ -3,14 +3,14 @@ use serde::{Deserialize, Serialize};
/// User registration Data
#[derive(Deserialize, Serialize)]
-pub struct AccountRegistration {
+pub struct AccountCreationInput {
pub username: String,
pub password: String,
pub retyped_password: String,
pub email: Option<String>,
}
-impl AccountRegistration {
+impl AccountCreationInput {
pub fn is_valid(&self) -> Result<(), ValidationError> {
if self.password != self.retyped_password {
return Err(ValidationError::Password);
diff --git a/phora/src/database/error.rs b/phora/src/database/error.rs
@@ -1,27 +1,27 @@
use std::fmt;
#[derive(Debug)]
-pub enum DatabaseError {
- ConnectionError,
- CreateTableError,
- AccountRetrievalError,
+pub enum Error {
+ Connection,
+ CreateTable,
+ AccountRetrieval,
}
-impl std::error::Error for DatabaseError {}
+impl std::error::Error for Error {}
-impl fmt::Display for DatabaseError {
+impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
- DatabaseError::ConnectionError => write!(f, "Database connection error"),
- DatabaseError::CreateTableError => write!(f, "Could not create Sql table"),
- DatabaseError::AccountRetrievalError => write!(f, "Could not create retrieve account"),
+ Error::Connection => write!(f, "Database connection error"),
+ Error::CreateTable => write!(f, "Could not create Sql table"),
+ Error::AccountRetrieval => write!(f, "Could not create retrieve account"),
}
}
}
#[cfg(feature = "sqlite")]
-impl From<rusqlite::Error> for DatabaseError {
+impl From<rusqlite::Error> for Error {
fn from(_: rusqlite::Error) -> Self {
- DatabaseError::ConnectionError
+ Error::Connection
}
}
diff --git a/phora/src/database/mod.rs b/phora/src/database/mod.rs
@@ -1,17 +1,15 @@
use crate::account::{Account, PublicAccount};
-use error::DatabaseError;
+use error::Error;
pub mod error;
#[cfg(feature = "sqlite")]
pub mod sqlite;
pub trait DB {
- /// New database instance
- fn new(path: &str) -> Self;
/// Connect to database
- fn connect(&mut self) -> Result<(), DatabaseError>;
+ fn connect(&mut self) -> Result<(), Error>;
/// Store user account
- fn store_account(&self, account: Account) -> Result<(), DatabaseError>;
+ fn account_store(&self, account: &Account) -> Result<(), Error>;
/// Get account public data
- fn get_public_account(&self, username: &str) -> Result<PublicAccount, DatabaseError>;
+ fn account_get_pub(&self, username: &str) -> Result<PublicAccount, Error>;
}
diff --git a/phora/src/database/sqlite.rs b/phora/src/database/sqlite.rs
@@ -1,4 +1,4 @@
-use super::{error::DatabaseError, DB};
+use super::{error::Error, DB};
use crate::account::{Account, PublicAccount};
use rusqlite::{params, Connection, Result};
@@ -36,16 +36,16 @@ pub struct SqliteDB {
/// An SQLite connection handle
conn: Option<Connection>,
}
-
-impl DB for SqliteDB {
- fn new(path: &str) -> Self {
+impl SqliteDB {
+ pub fn new(path: &str) -> Self {
SqliteDB {
path: path.to_string(),
conn: None,
}
}
-
- fn connect(&mut self) -> Result<(), DatabaseError> {
+}
+impl DB for SqliteDB {
+ fn connect(&mut self) -> Result<(), Error> {
// Open database connection
let conn = Connection::open(self.path.clone())?;
self.conn = Some(conn);
@@ -54,26 +54,26 @@ impl DB for SqliteDB {
match &self.conn {
Some(conn) => match conn.execute(accounts_sql::CREATE_TABLE, []) {
Ok(_rows) => Ok(()),
- Err(_err) => Err(DatabaseError::CreateTableError),
+ Err(_err) => Err(Error::CreateTable),
},
- None => Err(DatabaseError::ConnectionError),
+ None => Err(Error::Connection),
}
}
- fn store_account(&self, account: Account) -> Result<(), DatabaseError> {
+ fn account_store(&self, account: &Account) -> Result<(), Error> {
match &self.conn {
Some(conn) => {
conn.execute(
accounts_sql::STORE,
- params![account.username, account.password, account.email],
+ params![&account.username, account.password, account.email],
)?;
Ok(())
}
- None => Err(DatabaseError::ConnectionError),
+ None => Err(Error::Connection),
}
}
- fn get_public_account(&self, username: &str) -> Result<PublicAccount, DatabaseError> {
+ fn account_get_pub(&self, username: &str) -> Result<PublicAccount, Error> {
match &self.conn {
Some(conn) => {
let mut stmt = conn.prepare(accounts_sql::GET_PUBLIC)?;
@@ -82,10 +82,10 @@ impl DB for SqliteDB {
Some(s) => Ok(PublicAccount {
username: s.get(0)?,
}),
- None => Err(DatabaseError::AccountRetrievalError),
+ None => Err(Error::AccountRetrieval),
}
}
- None => Err(DatabaseError::ConnectionError),
+ None => Err(Error::Connection),
}
}
}
@@ -120,9 +120,9 @@ mod test {
};
db.connect().unwrap();
- db.store_account(account).unwrap();
+ db.account_store(&account).unwrap();
- let public_account = db.get_public_account("testuszee").unwrap();
+ let public_account = db.account_get_pub("testuszee").unwrap();
assert_eq!(&public_account.username, "testuszee");
}
diff --git a/phora/src/lib.rs b/phora/src/lib.rs
@@ -1,6 +1,56 @@
+use account::PublicAccount;
+use database::DB;
+
pub mod account;
-pub mod api;
pub mod crypto;
pub mod data;
pub mod database;
pub mod validate;
+
+pub struct Cyrtophora<D>
+where
+ D: DB,
+{
+ database: Option<D>,
+}
+
+impl<D: DB> Cyrtophora<D> {
+ #[cfg(feature = "sqlite")]
+ pub fn new(path: &str) -> Result<Self, database::error::Error> {
+ let mut db = database::sqlite::SqliteDB::new(path);
+ db.connect()?;
+ let c = Cyrtophora { database: Some(db) };
+ Ok(c)
+ }
+
+ /// Create a new account
+ pub fn account_create(
+ &mut self,
+ payload: data::AccountCreationInput,
+ ) -> Result<PublicAccount, Box<dyn std::error::Error>> {
+ let account = account::Account::create(payload)?;
+ // TODO: email verification
+
+ // store account in database
+ if cfg!(sqlite) {
+ if let Some(db) = &self.database {
+ db.account_store(&account)?;
+ }
+ }
+ Ok(account.into())
+ }
+
+ /// Get account public data, using the username as ID
+ pub fn account_get(&self, username: &str) -> Result<PublicAccount, database::error::Error> {
+ if cfg!(sqlite) {
+ if let Some(db) = &self.database {
+ let public_account = db.account_get_pub(username)?;
+ Ok(public_account)
+ } else {
+ Err(database::error::Error::Connection)
+ }
+ } else {
+ Err(database::error::Error::Connection)
+ }
+ }
+}
diff --git a/spec/book.toml b/spec/book.toml
@@ -3,4 +3,4 @@ authors = ["Jackson G. Kaindume"]
language = "en"
multilingual = false
src = "src"
-title = "cyrtophora"
+title = "Cyber Defense"
diff --git a/spec/src/SUMMARY.md b/spec/src/SUMMARY.md
@@ -5,3 +5,4 @@
- [Database](./database.md)
- [SQLite Support](./sqlite-support.md)
- [Password-hashing](./password-hashing.md)
+- [Validation](./validation.md)
diff --git a/spec/src/cyrtophora.md b/spec/src/cyrtophora.md
@@ -1,3 +1,3 @@
# Cyrtophora
-Full-stack users-first web framework.
+Full-stack users-first, secure web framework.
diff --git a/spec/src/password-hashing.md b/spec/src/password-hashing.md
@@ -69,3 +69,7 @@ password hash functions are used and implemented correctly even the
administrators of the server will not be able to read the users
passwords especially if the server is open source and the users can
audit the code for themselves.
+
+<https://www.troyhunt.com/our-password-hashing-has-no-clothes/>
+<https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016>
+<https://www.troyhunt.com/passwords-evolved-authentication-guidance-for-the-modern-era/>
diff --git a/spec/src/validation.md b/spec/src/validation.md
@@ -0,0 +1 @@
+<https://beesbuzz.biz/code/439-Falsehoods-programmers-believe-about-email>