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