NAME

Data::Passphrase::Rule - rule for validating passphrases

SYNOPSIS

my $rule = Data::Passphrase::Rule->new({
   code     => 450,
   message  => 'is too short',
   test     => 'X' x 15,
   validate => sub { $_[0] / 25 },
});

DESCRIPTION

Objects of this class represent individual strength-checking rules used by Data::Passphrase.

INTERFACE

There is a constructor, new, which takes a reference to a hash of initial attribute settings, and accessor methods of the form get_attribute() and set_attribute(). See "Attributes".

Attributes

code

Numeric status code returned if passphrase fails this rule.

message

Textual status message returned if passphrase fails this rule.

test

Passphrase(s) used to test this rule specified as a string, an anoymous array of strings (each of which is tested), or a hash. The hash is useful if you want each test phrase to return a different code and/or message, which is useful if the validation subroutine sets them to something other than their specification in the rule. Test passphrases themselves are specified as the keys of the hash. The values of the hash may be strings representing the messages associated with each test passphrase, or they may be hash references with code and message attributes. See "EXAMPLES".

A reference to a subroutine that returns any of the above may also be specified.

validate

Reference to a subroutine that does the validation and returns a score. This subroutine may override the code and/or message attributes by setting them excplitly. See "EXAMPLES".

EXAMPLES

For basic examples, see Data::Passphrase and the included passphrase_rules file.

Here's a more convoluted example. The validation subroutine in this rule sets the code and message attributes explicitly, which is useful to conditionally apply of certain checks or when an external application provides the code and/or message. This example makes use of Cracklib to test non-passphrases and does no complicated scoring -- passwords receive a score of 0 for failing or 1 for passing.

# invoke Cracklib
{
    code     => 470,
    message  => 'rejected by Cracklib',
    test     => sub {
        my ($self) = @_;

        my $username = $self->get_username() or return;

        return {
            "${username}!$%^&*()" => {
                code    => 472,
                message => 'may not be based on your username',
            },
            abcdefgh   => 'is too simplistic/systematic',
            password   => 'is based on a dictionary word',
            password1  => 'is based on a dictionary word',
            p455w0rd   => 'is based on a dictionary word',
            k1i988i7   => 'contains a repeating number or symbol',
            'k1i9]]i7' => 'contains a repeating number or symbol',
            179280398  => 'appears to be a Social Security Number',
        };
    },
    validate => sub {
        my ($self) = @_;

        # unpack attributes
        my $passphrase = $self->get_passphrase();
        my $username   = $self->get_username  ();

        # passphrases don't need to pass Cracklib
        return 1
            if length $passphrase >= $MINIMUM_PASSPHRASE_CHARACTERS;

        # use Cracklib to compare against username
        if (Crypt::Cracklib::GTry($username, $passphrase)) {
            $self->set_code   (472                                );
            $self->set_message('may not be based on your username');
            return 0;
        }

        # execute rest of Cracklib ruleset
        my $message = fascist_check $passphrase, $CRACKLIB_DICTIONARY;
        return 1 if $message eq 'ok';

        # normalize message and set the attributes
        $message =~ s/^it //;
        $message =~ s/^'s /is /;
        $self->set_message($message);

        return 0;
    },
}

The validate subroutine first checks the password length; if it's longer than a defined minimum, it's passed on to other rules that test passphrase strength. Then it tests the password for similary to the username (we've locally modified Crypt::Cracklib to expose the GTry() routine). If GTry() sees a similarity, the validate subroutine returns a special code and message. Finally, fascist_check() is called, and if it determines the password to be too weak, the validate subroutine passes the message along.

Our test routine provides one password to test the GTry() check. It pads the username with punctuation characters to ensure the minimum length (enforced by previous rules) is satisfied. The rest of the test passwords specify their own messages but inherit the code specified in the code attribute of the rule.

AUTHOR

Andrew J. Korty <ajk@iu.edu>

SEE ALSO

Crypt::Cracklib(3), Data::Passphrase(3), Data::Passphrase::Ruleset(3)