Eine Klasse zur Überprüfung der Passwortrichtlinie
authorMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 19 Jan 2011 10:15:53 +0000 (11:15 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 19 Jan 2011 10:15:53 +0000 (11:15 +0100)
Conflicts:

locale/de/all

SL/Auth/PasswordPolicy.pm [new file with mode: 0644]
config/lx_office.conf.default
locale/de/all
scripts/locales.pl

diff --git a/SL/Auth/PasswordPolicy.pm b/SL/Auth/PasswordPolicy.pm
new file mode 100644 (file)
index 0000000..866e49c
--- /dev/null
@@ -0,0 +1,168 @@
+package SL::Auth::PasswordPolicy;
+
+use strict;
+
+use parent qw(Rose::Object);
+
+use constant OK                   =>  0;
+use constant TOO_SHORT            =>  1;
+use constant TOO_LONG             =>  2;
+use constant MISSING_LOWERCASE    =>  4;
+use constant MISSING_UPPERCASE    =>  8;
+use constant MISSING_DIGIT        => 16;
+use constant MISSING_SPECIAL_CHAR => 32;
+use constant INVALID_CHAR         => 64;
+
+use Rose::Object::MakeMethods::Generic
+(
+ 'scalar --get_set_init' => 'config',
+);
+
+sub verify {
+  my ($self, $password) = @_;
+
+  my $cfg = $self->config;
+  return OK() unless $cfg && %{ $cfg };
+
+  my $result = OK();
+  $result |= TOO_SHORT()            if $cfg->{min_length}                && (length($password) < $cfg->{min_length});
+  $result |= TOO_LONG()             if $cfg->{max_length}                && (length($password) > $cfg->{max_length});
+  $result |= MISSING_LOWERCASE()    if $cfg->{require_lowercase}         && $password !~ m/[a-z]/;
+  $result |= MISSING_UPPERCASE()    if $cfg->{require_uppercase}         && $password !~ m/[A-Z]/;
+  $result |= MISSING_DIGIT()        if $cfg->{require_digit}             && $password !~ m/[0-9]/;
+  $result |= MISSING_SPECIAL_CHAR() if $cfg->{require_special_character} && $password !~ $cfg->{special_characters_re};
+  $result |= INVALID_CHAR()         if $cfg->{invalid_characters_re}     && $password =~ $cfg->{invalid_characters_re};
+
+  return $result;
+}
+
+sub errors {
+  my ($self, $result) = @_;
+
+  my @errors;
+
+  push @errors, $::locale->text('The password is too short (minimum length: #1).', $self->config->{min_length}) if $result & TOO_SHORT();
+  push @errors, $::locale->text('The password is too long (maximum length: #1).',  $self->config->{max_length}) if $result & TOO_LONG();
+  push @errors, $::locale->text('A lower-case character is required.')                                          if $result & MISSING_LOWERCASE();
+  push @errors, $::locale->text('An upper-case character is required.')                                         if $result & MISSING_UPPERCASE();
+  push @errors, $::locale->text('A digit is required.')                                                         if $result & MISSING_DIGIT();
+
+  if ($result & MISSING_SPECIAL_CHAR()) {
+    my $char_list = join ' ', sort split(m//, $self->config->{special_characters});
+    push @errors, $::locale->text('A special character is required (valid characters: #1).', $char_list);
+  }
+
+  if (($result & INVALID_CHAR())) {
+    my $char_list = join ' ', sort split(m//, $self->config->{ $self->config->{invalid_characters} ? 'invalid_characters' : 'valid_characters' });
+    push @errors, $::locale->text('An invalid character was used (invalid characters: #1).', $char_list) if $self->config->{invalid_characters};
+    push @errors, $::locale->text('An invalid character was used (valid characters: #1).',   $char_list) if $self->config->{valid_characters};
+  }
+
+  return @errors;
+}
+
+
+sub init_config {
+  my ($self) = @_;
+
+  my %cfg = %{ $::emmvee_conf{password_policy} || {} };
+
+  $cfg{valid_characters}      =~ s/[ \n\r]//g if $cfg{valid_characters};
+  $cfg{invalid_characters}    =~ s/[ \n\r]//g if $cfg{invalid_characters};
+  $cfg{invalid_characters_re} =  '[^' . quotemeta($cfg{valid_characters})   . ']' if $cfg{valid_characters};
+  $cfg{invalid_characters_re} =  '['  . quotemeta($cfg{invalid_characters}) . ']' if $cfg{invalid_characters};
+  $cfg{special_characters}    =  '!@#$%^&*()_+=[]{}<>\'"|\\,;.:?-';
+  $cfg{special_characters_re} =  '[' . quotemeta($cfg{special_characters}) . ']';
+  print $cfg{special_characters_re}, "\n";
+
+  map { $cfg{"require_${_}"} = $cfg{"require_${_}"} =~ m/^(?:1|true|t|yes|y)$/i } qw(lowercase uppercase digit special_char);
+
+  $self->config(\%cfg);
+}
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Auth::PasswordPolicy - Verify a given password against the policy
+set in the configuration file
+
+=head1 SYNOPSIS
+
+ my $verifier = SL::Auth::PasswordPolicy->new;
+ my $result   = $verifier->verify($password);
+ if ($result != SL::Auth::PasswordPolicy->OK()) {
+   print "Errors: " . join(' ', $verifier->errors($result)) . "\n";
+ }
+
+=head1 CONSTANTS
+
+=over 4
+
+=item C<OK>
+
+Password is OK.
+
+=item C<TOO_SHORT>
+
+The password is too short.
+
+=item C<TOO_LONG>
+
+The password is too long.
+
+=item C<MISSING_LOWERCASE>
+
+The password is missing a lower-case character.
+
+=item C<MISSING_UPPERCASE>
+
+The password is missing an upper-case character.
+
+=item C<MISSING_DIGIT>
+
+The password is missing a digit.
+
+=item C<MISSING_SPECIAL_CHAR>
+
+The password is missing a special character. Special characters are
+the following: ! " # $ % & ' ( ) * + , - . : ; E<lt> = E<gt> ? @ [ \ ]
+^ _ { | }
+
+=item C<INVALID_CHAR>
+
+The password contains an invalid character.
+
+=back
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<verify $password>
+
+Checks whether or not the password matches the policy. Returns C<OK()>
+if it does and an error code otherwise (binary or'ed of the error
+constants).
+
+=item C<errors $code>
+
+Returns an array of human-readable strings describing the issues set
+in C<$code> which should be the result of L</verify>.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index cd230d2..dd4dffa 100644 (file)
@@ -29,6 +29,31 @@ login = demo
 # location of history file for permanent history
 history_file = users/console_history
 
+# Settings used when the user changes his/her password. All options
+# default to no restriction if unset.
+[password_policy]
+# Minimum length in number of characters.
+min_length =
+# Maximum length in number of characters.
+max_length =
+# Require a lowe-case character?
+require_lowercase =
+# Require an upper-case character?
+require_uppercase =
+# Require a digit?
+require_digit =
+# Require a special char? Special chars are the following:
+# ! " # $ % & ' ( ) * + , - . : ; < = > ? @ [ \ ] ^ _ { | }
+require_special_character =
+# Optional list of valid characters. Spaces are ignored. If set then
+# the password must only consist of these characters.
+valid_characters =
+# Optional list of invalid characters. Spaces are ignored.
+invalid_characters =
+# Whether or not to check the policy if the password is set from the
+# user administration.
+disable_policy_for_admin =
+
 [debug]
 # Use DBIx::Log4perl for logging DBI calls. The string LXDEBUGFILE
 # will be replaced by the file name configured for $::lxdebug.
index 115222d..00d74fd 100644 (file)
@@ -38,9 +38,12 @@ $self->{texts} = {
   '4. Quarter'                  => '4. Quartal',
   '<b>What</b> do you want to look for?' => '<b>Wonach</b> wollen Sie suchen?',
   'A Buchungsgruppe consists of a descriptive name and the account numbers for the income and expense accounts for those four tax zones as well as the inventory account number.' => 'Eine Buchungsgruppe besteht aus einem deskriptiven Namen, den Erl&ouml;s- und Aufwandskonten f&uuml;r diese vier Steuerzonen sowie aus einem Inventarkonto.',
+  'A digit is required.'        => 'Eine Ziffer ist vorgeschrieben.',
   'A group named &quot;Full Access&quot; has been created.' => 'Eine Gruppe namens &quot;Vollzugriff&quot; wurde angelegt.',
   'A group with that name does already exist.' => 'Eine Gruppe mit diesem Namen gibt es bereits.',
   'A lot of the usability of Lx-Office has been enhanced with javascript. Although it is currently possible to use every aspect of Lx-Office without javascript, we strongly recommend it. In a future version this may change and javascript may be necessary to access advanced features.' => 'Die Bedienung von Lx-Office wurde an vielen Stellen mit Javascript verbessert. Obwohl es derzeit möglich ist, jeden Aspekt von Lx-Office auch ohne Javascript zu benutzen, empfehlen wir es. In einer zukünftigen Version wird Javascript eventuell notwendig sein um weitergehende Features zu benutzen.',
+  'A lower-case character is required.' => 'Ein Kleinbuchstabe ist vorgeschrieben.',
+  'A special character is required (valid characters: #1).' => 'Ein Sonderzeichen ist vorgeschrieben (gültige Zeichen: #1).',
   'A temporary directory could not be created:' => 'Ein tempor&auml;res Verzeichnis konnte nicht erstellt werden:',
   'A temporary file could not be created. Please verify that the directory "#1" is writeable by the webserver.' => 'Eine temporäre Datei konnte nicht angelegt werden. Bitte stellen Sie sicher, dass das Verzeichnis "#1" vom Webserver beschrieben werden darf.',
   'A temporary file could not be created:' => 'Eine tempor&auml;re Datei konnte nicht erstellt werden:',
@@ -180,6 +183,9 @@ $self->{texts} = {
   'Amount'                      => 'Betrag',
   'Amount Due'                  => 'Betrag fällig',
   'Amount has to be greater then zero! Wrong row number: ' => 'Leere Eingabe oder Werte kleiner, gleich null eingegeben. Fehler in Reihe Nummer: ',
+  'An invalid character was used (invalid characters: #1).' => 'Ein ungültiges Zeichen wurde benutzt (ungültige Zeichen: #1).',
+  'An invalid character was used (valid characters: #1).' => 'Ein ungültiges Zeichen wurde benutzt (gültige Zeichen: #1).',
+  'An upper-case character is required.' => 'Ein Großbuchstabe ist vorgeschrieben.',
   'Annotations'                 => 'Anmerkungen',
   'Another user with the login #1 does already exist.' => 'Es existiert bereits ein anderer Benutzer mit diesem Login.',
   'Ap aging on %s'              => 'Offene Verbindlichkeiten zum %s',
@@ -815,7 +821,6 @@ $self->{texts} = {
   'Help Template Variables'     => 'Hilfe zu Dokumenten-Variablen',
   'Here\'s an example command line:' => 'Hier ist eine Kommandozeile, die als Beispiel dient:',
   'Hide by default'             => 'Standardm&auml;&szlig;ig verstecken',
-  'History'                     => 'Historie',
   'History Search'              => 'Historien Suche',
   'History Search Engine'       => 'Historien Suchmaschine',
   'Homepage'                    => 'Homepage',
@@ -1679,6 +1684,8 @@ $self->{texts} = {
   'The parts have been removed.' => 'Die Waren wurden aus dem Lager entnommen.',
   'The parts have been stocked.' => 'Die Artikel wurden eingelagert.',
   'The parts have been transferred.' => 'Die Waren wurden umgelagert.',
+  'The password is too long (maximum length: #1).' => 'Das Passwort ist zu lang (maximale Länge: #1).',
+  'The password is too short (minimum length: #1).' => 'Das Password ist zu kurz (minimale Länge: #1).',
   'The payments have been posted.' => 'Die Zahlungen wurden gebucht.',
   'The pg_dump process could not be started.' => 'Der pg_dump-Prozess konnte nicht gestartet werden.',
   'The pg_restore process could not be started.' => 'Der pg_restore-Prozess konnte nicht gestartet werden.',
index b972a49..f03e5c6 100755 (executable)
@@ -31,7 +31,7 @@ parse_args();
 my $basedir      = "../..";
 my $locales_dir  = ".";
 my $bindir       = "$basedir/bin/mozilla";
-my @progdirs     = ( "$basedir/SL/Controller", "$basedir/SL/Template/Plugin" );
+my @progdirs     = ( "$basedir/SL/Controller", "$basedir/SL/Template/Plugin", "$basedir/SL/Auth" );
 my $dbupdir      = "$basedir/sql/Pg-upgrade";
 my $dbupdir2     = "$basedir/sql/Pg-upgrade2";
 my $menufile     = "menu.ini";