package FS::password_history;
use base qw( FS::Record );

use strict;
use FS::Record qw( qsearch qsearchs );
use Authen::Passphrase;

# the only bit of autogenerated magic in here
our @foreign_keys;
FS::UID->install_callback(sub {
    @foreign_keys = grep /__/, __PACKAGE__->dbdef_table->columns;
});

=head1 NAME

FS::password_history - Object methods for password_history records

=head1 SYNOPSIS

  use FS::password_history;

  $record = new FS::password_history \%hash;
  $record = new FS::password_history { 'column' => 'value' };

  $error = $record->insert;

  $error = $new_record->replace($old_record);

  $error = $record->delete;

  $error = $record->check;

=head1 DESCRIPTION

An FS::password_history object represents a current or past password used
by a login account, employee, or other account managed within Freeside.  
FS::password_history inherits from FS::Record.  The following fields are
currently supported:

=over 4

=item passwordnum - primary key

=item _password - the encrypted password, as an RFC2307-style string
("{CRYPT}$2a$08$..." or "{MD5}1ab201f..." or similar). This is a serialized
L<Authen::Passphrase> object.

=item created - the date the password was set to this value. The record with
the most recent created time is the current password.

=back

Plus one of the following foreign keys:

=over 4

=item svc_acct__svcnum

=item svc_dsl__svcnum

=item svc_alarm__svcnum

=item agent__agentnum

=item contact__contactnum

=item access_user__usernum

=back

=head1 METHODS

=over 4

=item new HASHREF

Creates a new password history record.  To add the record to the database,
see L<"insert">.

=cut

sub table { 'password_history'; }

=item insert

=item delete

=item replace OLD_RECORD

=item check

Checks all fields to make sure this is a valid password history record.  If
there is an error, returns the error, otherwise returns false.  Called by the
insert and replace methods.

=cut

sub check {
  my $self = shift;

  my $error = 
    $self->ut_numbern('passwordnum')
    || $self->ut_anything('_password')
    || $self->ut_numbern('create')
    || $self->ut_numbern('create')
  ;
  return $error if $error;

  # FKs are mutually exclusive
  my $fk_in_use;
  foreach my $fk ( @foreign_keys ) {
    if ( $self->get($fk) ) {
      $self->ut_numbern($fk);
      return "multiple records linked to this password_history" if $fk_in_use;
      $fk_in_use = $fk;
    }
  }

  $self->SUPER::check;
}

=item linked_acct

Returns the object that's using this password.

=cut

sub linked_acct {
  my $self = shift;

  foreach my $fk ( @foreign_keys ) {
    if ( my $val = $self->get($fk) ) {
      my ($table, $key) = split(/__/, $fk);
      return qsearchs($table, { $key => $val });
    }
  }
}

=item password_equals PASSWORD

Returns true if PASSWORD (plaintext) is the same as the one stored in the 
history record, false if not.

=cut

sub password_equals {

  my ($self, $check_password) = @_;

  # _password here is always LDAP-style.
  try {
    my $auth = Authen::Passphrase->from_rfc2307($self->_password);
    return $auth->match($check_password);
  } catch {
    # if there's somehow bad data in the _password field, then it doesn't
    # match anything. much better than having it match _everything_.
    warn "password_history #" . $self->passwordnum . ": $_";
    return '';
  }

}

=back

=head1 BUGS

=head1 SEE ALSO

L<FS::Record>

=cut

1;

