1 package FS::password_history;
2 use base qw( FS::Record );
5 use FS::Record qw( qsearch qsearchs );
6 use Authen::Passphrase;
8 # the only bit of autogenerated magic in here
10 FS::UID->install_callback(sub {
11 @foreign_keys = grep /__/, __PACKAGE__->dbdef_table->columns;
16 FS::password_history - Object methods for password_history records
20 use FS::password_history;
22 $record = new FS::password_history \%hash;
23 $record = new FS::password_history { 'column' => 'value' };
25 $error = $record->insert;
27 $error = $new_record->replace($old_record);
29 $error = $record->delete;
31 $error = $record->check;
35 An FS::password_history object represents a current or past password used
36 by a login account, employee, or other account managed within Freeside.
37 FS::password_history inherits from FS::Record. The following fields are
42 =item passwordnum - primary key
44 =item _password - the encrypted password, as an RFC2307-style string
45 ("{CRYPT}$2a$08$..." or "{MD5}1ab201f..." or similar). This is a serialized
46 L<Authen::Passphrase> object.
48 =item created - the date the password was set to this value. The record with
49 the most recent created time is the current password.
53 Plus one of the following foreign keys:
57 =item svc_acct__svcnum
61 =item svc_alarm__svcnum
65 =item contact__contactnum
67 =item access_user__usernum
77 Creates a new password history record. To add the record to the database,
82 sub table { 'password_history'; }
88 =item replace OLD_RECORD
92 Checks all fields to make sure this is a valid password history record. If
93 there is an error, returns the error, otherwise returns false. Called by the
94 insert and replace methods.
102 $self->ut_numbern('passwordnum')
103 || $self->ut_anything('_password')
104 || $self->ut_numbern('create')
105 || $self->ut_numbern('create')
107 return $error if $error;
109 # FKs are mutually exclusive
111 foreach my $fk ( @foreign_keys ) {
112 if ( $self->get($fk) ) {
113 $self->ut_numbern($fk);
114 return "multiple records linked to this password_history" if $fk_in_use;
124 Returns the object that's using this password.
131 foreach my $fk ( @foreign_keys ) {
132 if ( my $val = $self->get($fk) ) {
133 my ($table, $key) = split(/__/, $fk);
134 return qsearchs($table, { $key => $val });
139 =item password_equals PASSWORD
141 Returns true if PASSWORD (plaintext) is the same as the one stored in the
142 history record, false if not.
146 sub password_equals {
148 my ($self, $check_password) = @_;
150 # _password here is always LDAP-style.
152 my $auth = Authen::Passphrase->from_rfc2307($self->_password);
153 return $auth->match($check_password);
155 # if there's somehow bad data in the _password field, then it doesn't
156 # match anything. much better than having it match _everything_.
157 warn "password_history #" . $self->passwordnum . ": $_";
163 sub _upgrade_schema {
164 # clean up history records where linked_acct has gone away
166 for my $fk ( grep /__/, __PACKAGE__->dbdef_table->columns ) {
167 my ($table, $key) = split(/__/, $fk);
169 ( $fk IS NOT NULL AND NOT EXISTS(SELECT 1 FROM $table WHERE $table.$key = $fk) )";
172 'table' => 'password_history',
173 'extra_sql' => ' WHERE ' . join(' AND ', @where),
177 warn "Removing unattached password_history records (".scalar(@recs).").\n";
178 foreach my $password_history (@recs) {
179 $error = $password_history->delete;
180 die $error if $error;