RT#42235: Strict password requirments are not disabled for signup portal [respects...
[freeside.git] / FS / FS / Password_Mixin.pm
index eb1db81..b807081 100644 (file)
@@ -16,6 +16,8 @@ FS::UID->install_callback( sub {
     #eval "use Authen::Passphrase::BlowfishCrypt;";
 });
 
+our @pw_set;
+
 our $me = '[' . __PACKAGE__ . ']';
 
 our $BLOWFISH_COST = 10;
@@ -37,7 +39,7 @@ message on failure, an empty string on success.
 This MUST NOT be called from check(). It should be called by the office UI,
 self-service ClientAPI, or other I<user-interactive> code that processes a
 password change, and only if the user has taken some action with the intent
-of changing the password.
+of setting the password.
 
 =cut
 
@@ -45,6 +47,17 @@ sub is_password_allowed {
   my $self = shift;
   my $password = shift;
 
+  my $cust_main = $self->cust_main;
+
+  # workaround for non-inserted services
+  if ( !$cust_main and $self->get('pkgnum') ) {
+    my $cust_pkg = FS::cust_pkg->by_key($self->get('pkgnum'));
+    $cust_main = $cust_pkg->cust_main if $cust_pkg;
+  }
+  # selfservice signup invokes this without customer, but it checks this conf separately
+  warn "is_password_allowed: no customer could be identified" if !$cust_main;
+  return '' if $cust_main && $conf->config_bool('password-insecure', $cust_main->agentnum);
+
   # basic checks using Data::Password;
   # options for Data::Password
   $DICTIONARY = 4;   # minimum length of disallowed words
@@ -70,7 +83,6 @@ sub is_password_allowed {
   return '' unless $self->get($self->primary_key); # for validating new passwords pre-insert
 
   #check against customer fields
-  my $cust_main = $self->cust_main;
   if ($cust_main) {
     my @words;
     # words from cust_main
@@ -205,6 +217,26 @@ sub insert_password_history {
 
 }
 
+=item delete_password_history;
+
+Removes all password history records attached to this object, in preparation
+to delete the object.
+
+=cut
+
+sub delete_password_history {
+  my $self = shift;
+  my @records = qsearch('password_history', {
+      $self->password_history_key => $self->get($self->primary_key)
+  });
+  my $error = '';
+  foreach (@records) {
+    $error ||= $_->delete;
+  }
+  return $error . ' (clearing password history)' if $error;
+  '';
+}
+
 =item _blowfishcrypt PASSWORD
 
 For internal use: takes PASSWORD and returns a new
@@ -224,6 +256,37 @@ sub _blowfishcrypt {
 
 =back
 
+=head1 CLASS METHODS
+
+=over 4
+
+=item pw_set
+
+Returns the list of characters allowed in random passwords (from the
+C<password-generated-characters> config).
+
+=cut
+
+sub pw_set {
+  my $class = shift;
+  if (!@pw_set) {
+    my $pw_set = $conf->config('password-generated-characters');
+    $pw_set =~ s/\s//g; # don't ever allow whitespace
+    if ( $pw_set =~ /[[:lower:]]/
+      && $pw_set =~ /[[:upper:]]/
+      && $pw_set =~ /[[:digit:]]/
+      && $pw_set =~ /[[:punct:]]/ ) {
+      @pw_set = split('', $pw_set);
+    } else {
+      warn "password-generated-characters set is insufficient; using default.";
+      @pw_set = split('', 'abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789()#.,');
+    }
+  }
+  return @pw_set;
+}
+
+=back
+
 =head1 SEE ALSO
 
 L<FS::password_history>