Some prep for user sign in implementation + formatting changes
global changes: - config now uses snake_case instead of camelCase for config entry names - closing brackets/braces never occur on their own line - there are almost never spaces inside brackets/braces before and after the actual content - I also removed a bunch of linebreaks I really shouldn't have because it just ended up worsening readability, so I'm going to put them back tomorrow lol `lib/CharmBoard.pm` : - removed PostgreSQL from the database driver detector, I just wanna stick with SQLite and MySQL initially since I'm only really familiar with those - detect dev environment (from conf file setting) and only shut caching off in that situation - removed the default layout config option `lib/CharmBoard/Crypt/Password.pm` - renamed subroutines from snake_case to whateverthisiscalled - changed what format `passgen` outputs salts and hashes in - changed size and factor for reasons I honestly don't remember at this point. I should probably recalibrate that properly tomorrow - added `passchk` subroutine for verifying of passwords on the login screen - nice and helpful comments `lib/CharmBoard/Crypt/Seasoning.pm` - this is what used to be `tools/pepper.pl`. it's not currently used for anything but it will be used for pepper generation during setup if CharmBoard ever gets to that point. also might use it for generating salts actually `lib/CharmBoard/Controller/Auth.pm` - realized I had the salt and hash variable assignments the wrong way 'round like an idiot, so I fixed that - added part of signup (password auth) - also lots of hopefully-helpful comments? `lib/CharmBoard/Schema/*.pm` - added more params for each column `lib/CharmBoard/Schema/Session.pm` - added `is_ip_bound` and `bound_ip` columns `database.sql` - as for stuff not mentioned in the schema module changes, I added `ON CONFLICT` behavior (it's all `ABORT`, which rolls back the entire transaction) i'm tired i'm spenfing too much time either reading about mojolicious/perl or actually programming in them (usually the former atm) i need to chillax and play some videogames
This commit is contained in:
parent
5880920e5f
commit
c5785301ca
|
@ -8,8 +8,13 @@
|
|||
"Authen",
|
||||
"CharmBoard",
|
||||
"Facepunch",
|
||||
"passchk",
|
||||
"passgen",
|
||||
"pgsql",
|
||||
"resultset",
|
||||
"signup"
|
||||
"signup",
|
||||
"subf",
|
||||
"subforum",
|
||||
"subforums"
|
||||
]
|
||||
}
|
|
@ -4,13 +4,13 @@ Please keep in mind that CharmBoard is alpha software, and as such should not be
|
|||
|
||||
## Preparation
|
||||
|
||||
### Database types
|
||||
### Selecting a database type
|
||||
|
||||
CharmBoard supports two different types of databases. Below is a table listing off each type, as well as an explanation of the differences for people who are unsure which to pick.
|
||||
|
||||
| Name | config value | DBD package | Information |
|
||||
|-|-|-|-|
|
||||
| SQLite | `sqlite` | `DBD:SQLite` | Good for small installs (private forum with one friend group, etc.)<br />Easy to set up as the database is contained in a standalone file. |
|
||||
| SQLite | `sqlite` | `DBD:SQLite` | Good for small installs (i.e. a private forum with one friend group, etc.)<br />Easy to set up as the database is contained in a standalone file. |
|
||||
| MySQL | `mysql` | `DBD:mysql` | Has better performance on larger databases than SQLite does.<br />Harder to set up than SQLite as it requires the separate database server software to be set up alongside CharmBoard. |
|
||||
|
||||
### Installing dependencies
|
||||
|
|
16
README.md
16
README.md
|
@ -1,15 +1,17 @@
|
|||
# CharmBoard
|
||||
|
||||
CharmBoard is forum software written in Perl, inspired by AcmlmBoard/its derivatives, the original Facepunch forums, and Knockout.chat. It's intended to be a more "fun" alternative to the bigger forum software suites available today.
|
||||
CharmBoard is forum software written in Perl, inspired by AcmlmBoard/its derivatives, the original Facepunch forums, and Knockout.chat. It's intended to be a more "fun" alternative to the bigger forum software suites available today. Though largely, it's just intended as a sort of pet project of mine for now and a way to learn Perl + Mojolicious, and some other modules I've been wanting to learn.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Perl 5 (TODO: specific version reqs)
|
||||
- `Mojolicious` ([website](https://www.mojolicious.org/), [metacpan](https://metacpan.org/pod/Mojolicious))
|
||||
- `DBI`
|
||||
- `DBIx::Class`
|
||||
- one of two `DBD` database drivers — see `INSTALLING.md` for detailed information
|
||||
- `Authen::Passphrase::Argon2`
|
||||
- Perl5 v5.20.0 or higher
|
||||
- `Mojolicious` ([website](https://www.mojolicious.org/), [metacpan](https://metacpan.org/pod/Mojolicious))
|
||||
- `Mojolicious::Plugin::Renderer::WithoutCache` — only needed in dev environment
|
||||
- `DBI`
|
||||
- `DBIx::Class`
|
||||
- one of two `DBD` database drivers — see `INSTALLING.md` for detailed information
|
||||
- `Authen::Passphrase::Argon2`
|
||||
- `Math::Random::Secure`
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
{
|
||||
boardName => '', # this doesn't do anything yet
|
||||
board_name => '', # this doesn't do anything yet
|
||||
|
||||
database => {
|
||||
type => '', # 'sqlite', 'mysql', or 'pgsql'
|
||||
type => '', # 'sqlite' or 'mysql'
|
||||
name => '',
|
||||
user => '',
|
||||
pass => ''
|
||||
},
|
||||
|
||||
passCrypt => {
|
||||
pass_crypt => {
|
||||
pepper => '' # generate this with `tools/pepper.pl` for now
|
||||
},
|
||||
|
||||
environment => '', # only use 'dev' for now
|
||||
|
||||
secrets => ['']
|
||||
};
|
104
database.sql
104
database.sql
|
@ -1,5 +1,5 @@
|
|||
--
|
||||
-- File generated with SQLiteStudio v3.4.4 on Sat. May 6 00:01:26 2023
|
||||
-- File generated with SQLiteStudio v3.4.4 on Sun. May 7 00:02:05 2023
|
||||
--
|
||||
-- Text encoding used: UTF-8
|
||||
--
|
||||
|
@ -8,40 +8,104 @@ BEGIN TRANSACTION;
|
|||
|
||||
-- Table: categories
|
||||
DROP TABLE IF EXISTS categories;
|
||||
CREATE TABLE IF NOT EXISTS categories (cat_id INTEGER NOT NULL UNIQUE, cat_name TEXT, PRIMARY KEY (cat_id AUTOINCREMENT));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS categories (
|
||||
cat_id INTEGER NOT NULL ON CONFLICT ROLLBACK
|
||||
UNIQUE ON CONFLICT ROLLBACK,
|
||||
cat_name TEXT,
|
||||
PRIMARY KEY (
|
||||
cat_id AUTOINCREMENT
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
-- Table: posts
|
||||
DROP TABLE IF EXISTS posts;
|
||||
CREATE TABLE IF NOT EXISTS "posts" (
|
||||
"post_id" INTEGER NOT NULL UNIQUE,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"thread_id" INTEGER NOT NULL,
|
||||
"post_date" INTEGER NOT NULL,
|
||||
PRIMARY KEY("post_id" AUTOINCREMENT),
|
||||
FOREIGN KEY("user_id") REFERENCES "users"("user_id"),
|
||||
FOREIGN KEY("thread_id") REFERENCES "threads"("thread_id")
|
||||
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
post_id INTEGER NOT NULL ON CONFLICT ROLLBACK
|
||||
UNIQUE ON CONFLICT ROLLBACK,
|
||||
user_id INTEGER NOT NULL ON CONFLICT ROLLBACK,
|
||||
thread_id INTEGER NOT NULL ON CONFLICT ROLLBACK,
|
||||
post_date INTEGER NOT NULL ON CONFLICT ROLLBACK,
|
||||
PRIMARY KEY (
|
||||
post_id AUTOINCREMENT
|
||||
),
|
||||
FOREIGN KEY (
|
||||
user_id
|
||||
)
|
||||
REFERENCES users (user_id),
|
||||
FOREIGN KEY (
|
||||
thread_id
|
||||
)
|
||||
REFERENCES threads (thread_id)
|
||||
);
|
||||
|
||||
-- Table: session
|
||||
DROP TABLE IF EXISTS session;
|
||||
CREATE TABLE IF NOT EXISTS "session" (
|
||||
"user_id" INTEGER NOT NULL UNIQUE,
|
||||
"session_id" TEXT NOT NULL,
|
||||
"session_expiry" INTEGER,
|
||||
PRIMARY KEY("user_id")
|
||||
|
||||
-- Table: sessions
|
||||
DROP TABLE IF EXISTS sessions;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
user_id INTEGER PRIMARY KEY
|
||||
REFERENCES users (user_id)
|
||||
UNIQUE
|
||||
NOT NULL,
|
||||
session_key TEXT NOT NULL
|
||||
UNIQUE,
|
||||
session_expiry NUMERIC NOT NULL,
|
||||
is_ip_bound INTEGER (1, 1) NOT NULL
|
||||
DEFAULT (0),
|
||||
bound_ip TEXT
|
||||
);
|
||||
|
||||
|
||||
-- Table: subforums
|
||||
DROP TABLE IF EXISTS subforums;
|
||||
CREATE TABLE IF NOT EXISTS subforums (subf_id INTEGER PRIMARY KEY UNIQUE NOT NULL, subf_cat INTEGER REFERENCES categories (cat_id) UNIQUE NOT NULL, subf_name TEXT NOT NULL, subf_desc);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subforums (
|
||||
subf_id INTEGER PRIMARY KEY
|
||||
UNIQUE ON CONFLICT ROLLBACK
|
||||
NOT NULL ON CONFLICT ROLLBACK,
|
||||
subf_cat INTEGER REFERENCES categories (cat_id)
|
||||
UNIQUE ON CONFLICT ROLLBACK
|
||||
NOT NULL ON CONFLICT ROLLBACK,
|
||||
subf_name TEXT NOT NULL ON CONFLICT ROLLBACK,
|
||||
subf_desc
|
||||
);
|
||||
|
||||
|
||||
-- Table: threads
|
||||
DROP TABLE IF EXISTS threads;
|
||||
CREATE TABLE IF NOT EXISTS threads (thread_id INTEGER NOT NULL, thread_title TEXT NOT NULL, thread_subf INTEGER REFERENCES categories (cat_id), PRIMARY KEY (thread_id AUTOINCREMENT));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS threads (
|
||||
thread_id INTEGER NOT NULL ON CONFLICT ROLLBACK,
|
||||
thread_title TEXT NOT NULL ON CONFLICT ROLLBACK,
|
||||
thread_subf INTEGER REFERENCES categories (cat_id),
|
||||
PRIMARY KEY (
|
||||
thread_id AUTOINCREMENT
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
-- Table: users
|
||||
DROP TABLE IF EXISTS users;
|
||||
CREATE TABLE IF NOT EXISTS users (user_id INTEGER NOT NULL UNIQUE, username TEXT NOT NULL UNIQUE ON CONFLICT ABORT, email TEXT UNIQUE NOT NULL, password INTEGER NOT NULL, salt TEXT NOT NULL, signup_date INTEGER NOT NULL, PRIMARY KEY (user_id AUTOINCREMENT) ON CONFLICT FAIL);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id INTEGER NOT NULL ON CONFLICT ROLLBACK
|
||||
UNIQUE ON CONFLICT ROLLBACK,
|
||||
username TEXT NOT NULL ON CONFLICT ROLLBACK
|
||||
UNIQUE ON CONFLICT ROLLBACK,
|
||||
email TEXT UNIQUE ON CONFLICT ROLLBACK
|
||||
NOT NULL ON CONFLICT ROLLBACK,
|
||||
password TEXT NOT NULL ON CONFLICT ROLLBACK,
|
||||
salt TEXT NOT NULL ON CONFLICT ROLLBACK,
|
||||
signup_date REAL NOT NULL,
|
||||
PRIMARY KEY (
|
||||
user_id AUTOINCREMENT
|
||||
)
|
||||
ON CONFLICT ABORT
|
||||
);
|
||||
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
PRAGMA foreign_keys = on;
|
||||
|
|
|
@ -3,84 +3,65 @@ use experimental 'smartmatch';
|
|||
use Mojo::Base 'Mojolicious', -signatures;
|
||||
use CharmBoard::Schema;
|
||||
|
||||
# This method will run once at server start
|
||||
# this method will run once at server start
|
||||
sub startup ($app) {
|
||||
|
||||
$app = shift;
|
||||
|
||||
# load plugins that require no additional conf
|
||||
$app->plugin('TagHelpers');
|
||||
$app->plugin('Renderer::WithoutCache'); # for dev env only
|
||||
|
||||
$app->renderer->cache->max_keys(0); # for dev env only
|
||||
# load configuration from config file
|
||||
my $config = $app->plugin('Config' => {file => 'charmboard.conf'});
|
||||
|
||||
$app->defaults(layout => 'default');
|
||||
|
||||
# Load configuration from config file
|
||||
my $config = $app->plugin('Config' => {
|
||||
file => 'charmboard.conf'
|
||||
});
|
||||
# load dev env only stuff, if applicable
|
||||
if ( $config->{environment} eq 'dev' ) {
|
||||
$app->plugin('Renderer::WithoutCache');
|
||||
$app->renderer->cache->max_keys(0)};
|
||||
|
||||
# Configure the application
|
||||
## Import Mojolicious secrets (cookie encryption)
|
||||
# import Mojolicious secrets
|
||||
$app->secrets($config->{secrets});
|
||||
## Import password pepper value
|
||||
my $pepper = $config->{passCrypt}->{pepper};
|
||||
$app->helper( pepper => sub { $pepper } );
|
||||
## Database setup
|
||||
my ($dsn, $dbUnicode);
|
||||
|
||||
# import password pepper value
|
||||
$app->helper(pepper => sub {$config->{pass_crypt}->{pepper}});
|
||||
|
||||
## database setup
|
||||
my ($dsn, $dbUnicode);
|
||||
if ($app->config->{database}->{type} ~~ 'sqlite') {
|
||||
$dsn = "dbi:SQLite:" . $config->{database}->{name};
|
||||
$dbUnicode = "sqlite_unicode";
|
||||
} elsif ($app->config->{database}->{type} ~~ 'mysql') {
|
||||
$dbUnicode = "sqlite_unicode"}
|
||||
elsif ($app->config->{database}->{type} ~~ 'mysql') {
|
||||
$dsn = "dbi:mysql:" . $config->{database}->{name};
|
||||
$dbUnicode = "mysql_enable_utf";
|
||||
} elsif ($app->config->{database}->{type} ~~ 'pgsql') {
|
||||
$dsn = "dbi:Pg:" . $config->{database}->{name};
|
||||
$dbUnicode = "pg_enable_utf8";
|
||||
} else { die "\nUnknown, unsupported, or empty database type in charmboard.conf.
|
||||
$dbUnicode = "mysql_enable_utf"}
|
||||
else {die "\nUnknown, unsupported, or empty database type in charmboard.conf.
|
||||
If you're sure you've set it to something supported, maybe double check your spelling?\n
|
||||
Valid options: 'sqlite', 'mysql'"
|
||||
};
|
||||
|
||||
Valid options: 'sqlite', 'mysql'"};
|
||||
my $schema = CharmBoard::Schema->connect(
|
||||
$dsn,
|
||||
$config->{database}->{user},
|
||||
$config->{database}->{pass},
|
||||
{
|
||||
$dbUnicode => 1
|
||||
}
|
||||
);
|
||||
{$dbUnicode => 1});
|
||||
$app->helper(schema => sub {$schema});
|
||||
|
||||
$app->helper( schema => sub { $schema } );
|
||||
|
||||
# Router
|
||||
# router
|
||||
my $r = $app->routes;
|
||||
|
||||
# Controller routes
|
||||
## Index page
|
||||
# controller routes
|
||||
## index page
|
||||
$r->get('/')->to(
|
||||
controller => 'Controller::Main',
|
||||
action => 'index'
|
||||
);
|
||||
## Registration page
|
||||
action => 'index');
|
||||
## registration page
|
||||
$r->get('/register')->to(
|
||||
controller => 'Controller::Auth',
|
||||
action => 'register'
|
||||
);
|
||||
action => 'register');
|
||||
$r->post('/register')->to(
|
||||
controller => 'Controller::Auth',
|
||||
action => 'registration_do'
|
||||
);
|
||||
## Login page
|
||||
action => 'register_do');
|
||||
## login page
|
||||
$r->get('/login')->to(
|
||||
controller => 'Controller::Auth',
|
||||
action => 'login'
|
||||
);
|
||||
action => 'login');
|
||||
$r->post('/login')->to(
|
||||
controller => 'Controller::Auth',
|
||||
action => 'login_do'
|
||||
)
|
||||
action => 'login_do');
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package CharmBoard::Controller::Auth;
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
use CharmBoard::Crypt::Password;
|
||||
use CharmBoard::Crypt::Seasoning;
|
||||
use Time::HiRes qw(time);
|
||||
|
||||
# initial registration page
|
||||
sub register ($app) {
|
||||
|
@ -11,63 +13,80 @@ sub register ($app) {
|
|||
)};
|
||||
|
||||
# process submitted registration form
|
||||
sub registration_do ($app) {
|
||||
# TODO: implement email validation here at some point
|
||||
sub register_do ($app) {
|
||||
my $username = $app->param('username');
|
||||
my $email = $app->param('email');
|
||||
my $password = $app->param('password');
|
||||
my $confirmPassword = $app->param('confirm-password');
|
||||
|
||||
# check to make sure all required fields are filled
|
||||
if ( ! $username || ! $password || ! $confirmPassword ) {
|
||||
$app->flash( error => 'All fields required.' );
|
||||
$app->redirect_to('register');
|
||||
};
|
||||
if (! $username || ! $password || ! $confirmPassword) {
|
||||
$app->flash(error => 'All fields required.');
|
||||
$app->redirect_to('register')};
|
||||
|
||||
# check to make sure both passwords match
|
||||
# TODO: add check on frontend for this for people with JS enabled
|
||||
if ( $confirmPassword ne $password ) {
|
||||
$app->flash( error => 'Passwords do not match.' );
|
||||
$app->redirect_to('register');
|
||||
};
|
||||
if ($confirmPassword ne $password) {
|
||||
$app->flash(error => 'Passwords do not match.');
|
||||
$app->redirect_to('register')};
|
||||
|
||||
# check to make sure username and/or email isn't already in use;
|
||||
# if not, continue with registration
|
||||
## search for input username and email in database
|
||||
my $userCheck = $app->schema->resultset('Users')->search({username => $username})->single;
|
||||
my $emailCheck = $app->schema->resultset('Users')->search({email => $email})->single;
|
||||
if ( $userCheck || $emailCheck ) {
|
||||
if ( $userCheck && $emailCheck ) {
|
||||
# notify user that username and email are both already being used
|
||||
$app->flash( error => 'Username and email already in use.' );
|
||||
$app->redirect_to('register');
|
||||
} elsif ( $userCheck ) {
|
||||
# notify user that only username is already in use
|
||||
$app->flash( error => 'Username is already in use.' );
|
||||
$app->redirect_to('register');
|
||||
} elsif ( $emailCheck ) {
|
||||
# notify user that only email is already in use
|
||||
$app->flash( error => 'email is already in use.' );
|
||||
$app->redirect_to('register');
|
||||
}
|
||||
} else {
|
||||
|
||||
if ($userCheck || $emailCheck) {
|
||||
if ($userCheck && $emailCheck) { # notify user that username and email are both already being used
|
||||
$app->flash(error => 'Username and email already in use.');
|
||||
$app->redirect_to('register')}
|
||||
elsif ($userCheck) { # notify user that only username is already in use
|
||||
$app->flash(error => 'Username is already in use.');
|
||||
$app->redirect_to('register')}
|
||||
elsif ($emailCheck) { # notify user that only email is already in use
|
||||
$app->flash(error => 'email is already in use.');
|
||||
$app->redirect_to('register')}}
|
||||
else { # TODO: add more error handling here, in case SQL transact fails
|
||||
# append pepper to pass before hashing
|
||||
$password = $app->pepper . ':' . $password;
|
||||
my ($hash, $salt) = pass_gen($password);
|
||||
# return hashed result + salt
|
||||
my ($salt, $hash) = passgen($password);
|
||||
# add user info and pw/salt to DB
|
||||
$app->schema->resultset('Users')->create({
|
||||
username => $username,
|
||||
email => $email,
|
||||
password => $hash,
|
||||
salt => $salt,
|
||||
signup_date => time
|
||||
});
|
||||
$app->flash( message => 'User registered successfully!' );
|
||||
$app->redirect_to('register');
|
||||
}};
|
||||
signup_date => time });
|
||||
$app->flash(message => 'User registered successfully!');
|
||||
$app->redirect_to('register')}};
|
||||
|
||||
sub login ($app) {
|
||||
$app->render(
|
||||
template => 'login',
|
||||
error => $app->flash('error'),
|
||||
message => $app->flash('message')
|
||||
);
|
||||
message => $app->flash('message'))};
|
||||
|
||||
sub login_do ($app) {
|
||||
my $username = $app->param('username');
|
||||
my $password = $app->param('password');
|
||||
$password = $app->pepper . ':' . $password;
|
||||
my $userInfoCheck = $app->schema->resultset('Users')->search({username => $username});
|
||||
if ($userInfoCheck) {
|
||||
my $savedSalt = $userInfoCheck->get_column('salt')->first;
|
||||
my $savedHash = $userInfoCheck->get_column('password')->first;
|
||||
my $passCheckStatus = passchk($savedSalt, $savedHash, $password);
|
||||
if ($passCheckStatus) {
|
||||
$app->flash(message => 'Password correct, but auth isn\'t implemented yet');
|
||||
$app->redirect_to('login')
|
||||
} else {
|
||||
$app->flash(error => 'Password incorrect');
|
||||
$app->redirect_to('login')}}
|
||||
else {
|
||||
$app->flash(error => 'User ' . $username . ' does not exist.');
|
||||
$app->redirect_to('login')};
|
||||
|
||||
}
|
||||
|
||||
1;
|
|
@ -2,7 +2,6 @@ package CharmBoard::Controller::Main;
|
|||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub index ($app) {
|
||||
$app->render(template => 'index');
|
||||
}
|
||||
$app->render(template => 'index')}
|
||||
|
||||
1;
|
|
@ -2,20 +2,35 @@ package CharmBoard::Crypt::Password;
|
|||
use Authen::Passphrase::Argon2;
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw(passgen passchk);
|
||||
|
||||
our @EXPORT = qw(pass_gen);
|
||||
|
||||
sub pass_gen ($) {
|
||||
# subroutine to generate password salt + hashed pw on pass creation
|
||||
# outputs the salt and then the hashed pw, so when assigning vars
|
||||
# from this sub's output, do it like this:
|
||||
# `my ($salt, $hash) = passgen($password);`
|
||||
sub passgen ($) {
|
||||
my $argon2 = Authen::Passphrase::Argon2->new(
|
||||
salt_random => 1,
|
||||
passphrase => $_[0],
|
||||
cost => 3,
|
||||
factor => '16M',
|
||||
passphrase => $_[0],
|
||||
cost => 3,
|
||||
factor => '32M',
|
||||
parallelism => 1,
|
||||
size => 32
|
||||
);
|
||||
size => 16 );
|
||||
|
||||
return ($argon2->salt_hex, $argon2->as_hex);
|
||||
}
|
||||
return ($argon2->salt, $argon2->as_crypt)};
|
||||
|
||||
# subroutine to check inputted password against one in DB
|
||||
# `$_[0]` is the salt, `$_[1]` is the hashed pass, and
|
||||
# `$_[2]` is the inputted plaintext pepper:password to check
|
||||
sub passchk ($$$) {
|
||||
my $argon2 = Authen::Passphrase::Argon2->new(
|
||||
salt => $_[0],
|
||||
hash => $_[1],
|
||||
cost => 3,
|
||||
factor => '32M',
|
||||
parallelism => 1,
|
||||
size => 16 );
|
||||
|
||||
return ($argon2->match($_[2]))}
|
||||
|
||||
1;
|
|
@ -0,0 +1,20 @@
|
|||
package CharmBoard::Crypt::Seasoning;
|
||||
use Math::Random::Secure qw(irand);
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw(seasoning);
|
||||
|
||||
sub seasoning ($) {
|
||||
my @spices = qw(0 1 2 3 4 5 6 7 8 9 a b c d e f g
|
||||
h i j k l m n o p q r s t u v w x y z A B C D E F
|
||||
G H I J K L M N O P Q R S T U V W X Y Z ! @ $ % ^
|
||||
& * / ? . ; : \ [ ] - _ < > ` ~ + = £ ¥ ¢);
|
||||
|
||||
my $blend;
|
||||
while (length($blend) < $_[0]) {
|
||||
# gen num to choose char for $blend
|
||||
$blend = $blend . $spices[irand(@spices)]};
|
||||
|
||||
return ($blend); }
|
||||
|
||||
1;
|
|
@ -6,12 +6,10 @@ __PACKAGE__->add_columns(
|
|||
cat_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
cat_name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
});
|
||||
is_nullable => 0, });
|
||||
__PACKAGE__->set_primary_key('cat_id');
|
||||
|
||||
1
|
|
@ -6,33 +6,27 @@ __PACKAGE__->add_columns(
|
|||
post_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
user_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
thread_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
post_date => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
});
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, });
|
||||
__PACKAGE__->set_primary_key('post_id');
|
||||
__PACKAGE__->belongs_to(
|
||||
user_id =>
|
||||
'CharmBoard::Schema::Result::Users',
|
||||
'user_id'
|
||||
);
|
||||
'user_id' );
|
||||
__PACKAGE__->belongs_to(
|
||||
thread_id =>
|
||||
'CharmBoard::Schema::Result::Threads',
|
||||
'thread_id'
|
||||
);
|
||||
'thread_id' );
|
||||
|
||||
1
|
|
@ -1,25 +1,32 @@
|
|||
package CharmBoard::Schema::Result::Session;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('session');
|
||||
__PACKAGE__->table('sessions');
|
||||
__PACKAGE__->add_columns(
|
||||
user_id => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
session_id => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
session_key => {
|
||||
data_type => 'text',
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
session_expiry => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
});
|
||||
data_type => 'numeric',
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
is_ip_bound => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
bound_ip => {
|
||||
data_type => 'text',
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 1, });
|
||||
__PACKAGE__->set_primary_key('user_id');
|
||||
__PACKAGE__->belongs_to(
|
||||
user_id =>
|
||||
'CharmBoard::Schema::Result::Users',
|
||||
'user_id'
|
||||
);
|
||||
'user_id');
|
||||
|
||||
1
|
|
@ -6,25 +6,23 @@ __PACKAGE__->add_columns(
|
|||
subf_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
subf_cat => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
subf_name => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
subf_desc => {
|
||||
data_type => 'text',
|
||||
is_nullable => 1,
|
||||
});
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 1, });
|
||||
__PACKAGE__->set_primary_key('subf_id');
|
||||
__PACKAGE__->belongs_to(
|
||||
subf_cat =>
|
||||
'CharmBoard::Schema::Result::Categories',
|
||||
{'foreign.cat_id' => 'self.subf_cat'}
|
||||
);
|
||||
{'foreign.cat_id' => 'self.subf_cat'});
|
||||
|
||||
1
|
|
@ -6,21 +6,17 @@ __PACKAGE__->add_columns(
|
|||
thread_id => {
|
||||
data_type => 'integer',
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
thread_title => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0,
|
||||
},
|
||||
is_nullable => 0, },
|
||||
thread_subf => {
|
||||
data_type => 'integer',
|
||||
is_nullable => 1,
|
||||
});
|
||||
is_nullable => 1, });
|
||||
__PACKAGE__->set_primary_key('thread_id');
|
||||
__PACKAGE__->belongs_to(
|
||||
thread_subf =>
|
||||
'CharmBoard::Schema::Result::Subforums',
|
||||
{'foreign.subf_id' => 'self.thread_subf'}
|
||||
);
|
||||
{'foreign.subf_id' => 'self.thread_subf'});
|
||||
|
||||
1
|
|
@ -7,28 +7,23 @@ __PACKAGE__->add_columns(
|
|||
data_type => 'integer',
|
||||
is_numeric => 1,
|
||||
is_nullable => 0,
|
||||
is_auto_increment => 1
|
||||
},
|
||||
is_auto_increment => 1, },
|
||||
username => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0
|
||||
},
|
||||
email => {
|
||||
data_type => 'text'
|
||||
},
|
||||
is_nullable => 0, },
|
||||
email => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0, },
|
||||
password => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0
|
||||
},
|
||||
is_nullable => 0, },
|
||||
salt => {
|
||||
data_type => 'text',
|
||||
is_nullable => 0
|
||||
},
|
||||
is_nullable => 0, },
|
||||
signup_date => {
|
||||
data_type => 'integer',
|
||||
data_type => 'real',
|
||||
is_numeric => 1,
|
||||
is_nullable => 0
|
||||
});
|
||||
is_nullable => 0, });
|
||||
__PACKAGE__->set_primary_key('user_id');
|
||||
|
||||
1
|
|
@ -1,25 +0,0 @@
|
|||
#!/usr/bin/env perl
|
||||
use warnings;
|
||||
use strict;
|
||||
use Math::Random::Secure qw( irand );
|
||||
|
||||
my @chars = qw( 0 1 2 3 4 5 6 7 8 9
|
||||
a b c d e f g h i j
|
||||
k l m n o p q r s t
|
||||
u v w x y z A B C D
|
||||
E F G H I J K L M N
|
||||
O P Q R S T U V W X
|
||||
Y Z ! @ $ % ^ & * /
|
||||
? . ; : \ [ ] - _ );
|
||||
|
||||
my $pepper = '';
|
||||
while ( length($pepper) < 25 ) {
|
||||
# gen and discard numbers to flush out dupe chance
|
||||
irand(255); irand(255); irand(255); irand(255);
|
||||
irand(255); irand(255); irand(255); irand(255);
|
||||
# gen num for pepper
|
||||
$pepper = $pepper . $chars[irand(@chars)];
|
||||
}
|
||||
|
||||
print("Your pepper value is:\n");
|
||||
print($pepper);
|
Loading…
Reference in New Issue