I have developed a module that has sort of evolved with me through
several projects.  On the most recent evolution it has reached a point
that I think it might be generally useful as a CPAN module.

The module models a user login with a DBIx::Connector back-end.

Logins authenticate using both passphrase, and optionally per-user, IP
whitelists.  Passphrases are always required.  Individual users may be
required to also match an IP whitelist set up for that user.
Different users may have different whitelists, or no whitelist
requirement at all.  Passphrases are salted with a 512 bit random salt
(different for each user), and hashed with a SHA2-512 digest.

User credentials include userid, salt, passphrase hash, email, user
name, optional IP whitelists, optional user roles.

Public API methods include:

my $user_obj = new( $dbix_connector, $userid )
my $user_id  = $user->add_user(
       {
           password => $password,    # Passphrase will get a 512 bit
random salt, and will be digested via SHA-512.
           ip_req   => $bool_ip_req,   # Optional Boolean: Is there an
IP whitelist requirement for this user?
           ips      => [ '192.168.0.100', '201.202.100.5' ], #
Optional aref of whitelisted ip's.
           username => $full_name,  # User's full name (optional field)
           email    => $email,           # User's email (optional field)
       }
);
my $userid      = $user->userid;
my $validated   = $user->validated;        # Test whether user has
been validated.
my $invalidated = $user->validated(0);    # Invalidate user.
my $is_valid    = $user->validate_user( $pass, $opt_ips_aref );
my $info_href        = $user->load_user;         # Load a hashref
containing userid, username, email, other credentials excluding
pass-hashes.
my $valid_ips_aref   = $user->fetch_valid_ips; # Return aref of
whitelist IPs for user (if applicable).
my $user_exists = $user->exists_user;
my $success     = $user->delete_user;
my $del_count   = $user->delete_ips( [ @ips ] );  # Delete one or more
whitelist IP's (if applicable).
my $add_count   = $user->add_ips( [ @ips ] );    # Add one or more
whitelist IP's (if applicable).
my $new_email   = $user->update_email( 'new@email.address' );
my $new_name    = $user->update_username( 'Cool New User Name' );
my $success      = $user->update_password( 'Old Pass', 'New Pass' );
my $cans_aref = $user->fetch_roles;
my $success      = $user->add_roles( [ @new_roles ] );
my $success     = $user->delete_roles( [ @roles ] );
my $can_do    = $user->can_role( [ @roles_to_test ] );


Passphrases for new users are salted with a 512 bit random salt using
a cryptographically strong random number generator, and digested using
SHA2-512 (Authen::Passphrase::SaltedSHA512).  Salt and Hash are stored
in a db along with userid, username, email, optional boolean setting
whether or not IP whitelisting is to be used in validation.

A second table associates valid IP's with userid's, and is used if the
ip_req field is true in the users table.

A third table handles user roles.  A user might have multiple roles.
This facilitates providing user access to certain portions of an
application, while denying (or rendering differently) for other
portions of an application depending on what roles the user can
do/access.

My most recent use-case was on a Mojolicious project, where it
integrated well with Mojolicious::Plugin::Authentication and
Mojolicious::Plugin::Authorization.  But I think it's general enough
to fit into other applications and other frameworks.

I've used this in several projects, and am finally getting around to
making it database agnostic, though it does need to be handed a
DBIx::Connector object to use.  The DBIx::Connector requirement is
hard wired; it uses DBIx::Connector's nice (and reliable) 'fixup',
'run' and 'txn' features for safe atomic commits in a preforking (aka
Hypnotoad server) environment.  The SQL resides in a helper module so
that all queries can be seen and if necessary edited in one compact
location.  I'll be converting its test suite to a BUILD_REQUIRES of
DBD::SQLite for testing purposes prior to packaging it up as a
distribution.

Now for the questions:

Will this be general enough to be useful on CPAN?

What would you call it?  (One individual suggested
User::Credentials::Provider::DBI.  In my own personal use I've been
calling it DBIx::Connector::Model::User.)

What have you been using?

While there is a design goal of keeping it as simple as practical,
what features seem to be missing?


Dave


-- 

David Oswald
daosw...@gmail.com

Reply via email to