2015-01-12 11:17:34 +01:00
|
|
|
#
|
2015-02-12 15:18:18 +01:00
|
|
|
# Copyright (c) 2011-2015 T.v.Dein <tlinden |AT| cpan.org>.
|
|
|
|
|
#
|
|
|
|
|
# Licensed under the terms of the Artistic License 2.0
|
|
|
|
|
# see: http://www.perlfoundation.org/artistic_license_2_0
|
2015-01-12 11:17:34 +01:00
|
|
|
#
|
2012-07-20 12:58:07 +02:00
|
|
|
package Crypt::PWSafe3::Record;
|
|
|
|
|
|
|
|
|
|
use Carp::Heavy;
|
|
|
|
|
use Carp;
|
|
|
|
|
use Exporter ();
|
|
|
|
|
use vars qw(@ISA @EXPORT %map2name %map2type);
|
|
|
|
|
|
|
|
|
|
my %map2type = %Crypt::PWSafe3::Field::map2type;
|
|
|
|
|
|
|
|
|
|
my %map2name = %Crypt::PWSafe3::Field::map2name;
|
|
|
|
|
|
2015-05-21 11:10:42 +02:00
|
|
|
$Crypt::PWSafe3::Record::VERSION = '1.10';
|
2012-07-20 12:58:07 +02:00
|
|
|
|
|
|
|
|
foreach my $field (keys %map2type ) {
|
|
|
|
|
eval qq(
|
|
|
|
|
*Crypt::PWSafe3::Record::$field = sub {
|
|
|
|
|
my(\$this, \$arg) = \@_;
|
|
|
|
|
if (\$arg) {
|
|
|
|
|
return \$this->modifyfield("$field", \$arg);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return \$this->{field}->{$field}->{value};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub new {
|
|
|
|
|
#
|
|
|
|
|
# new record object
|
2015-05-21 11:10:42 +02:00
|
|
|
my($this, %param) = @_;
|
2012-07-20 12:58:07 +02:00
|
|
|
my $class = ref($this) || $this;
|
2015-05-21 11:10:42 +02:00
|
|
|
my $self = \%param;
|
2012-07-20 12:58:07 +02:00
|
|
|
bless($self, $class);
|
|
|
|
|
$self->{field} = ();
|
|
|
|
|
|
|
|
|
|
# just in case this is a record to be filled by the user,
|
|
|
|
|
# initialize it properly
|
|
|
|
|
my $newuuid = $self->genuuid();
|
2012-07-20 13:40:43 +02:00
|
|
|
|
|
|
|
|
my $time = time;
|
|
|
|
|
|
2015-02-16 11:41:35 +01:00
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'uuid',
|
|
|
|
|
raw => $newuuid,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'ctime',
|
|
|
|
|
value => $time,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'mtime',
|
|
|
|
|
value => $time
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'lastmod',
|
|
|
|
|
value => $time
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'passwd',
|
|
|
|
|
value => ''
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'user',
|
|
|
|
|
value => ''
|
2012-07-20 12:58:07 +02:00
|
|
|
));
|
|
|
|
|
|
2015-02-16 11:41:35 +01:00
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'title',
|
|
|
|
|
value => ''
|
2012-07-20 12:58:07 +02:00
|
|
|
));
|
|
|
|
|
|
2015-02-16 11:41:35 +01:00
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'notes',
|
|
|
|
|
value => ''
|
|
|
|
|
));
|
2012-07-20 12:58:07 +02:00
|
|
|
|
2015-02-16 11:41:35 +01:00
|
|
|
$self->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'group',
|
|
|
|
|
value => ''
|
|
|
|
|
));
|
2012-07-20 13:40:43 +02:00
|
|
|
|
2012-07-20 12:58:07 +02:00
|
|
|
return $self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub modifyfield {
|
|
|
|
|
#
|
|
|
|
|
# add or modify a record field
|
|
|
|
|
my($this, $name, $value) = @_;
|
|
|
|
|
if (exists $map2type{$name}) {
|
|
|
|
|
my $type = $map2type{$name};
|
2015-02-16 11:41:35 +01:00
|
|
|
my $field = Crypt::PWSafe3::Field->new(
|
2012-07-20 12:58:07 +02:00
|
|
|
type => $type,
|
|
|
|
|
value => $value
|
|
|
|
|
);
|
2012-07-20 13:40:43 +02:00
|
|
|
|
|
|
|
|
my $time = time;
|
|
|
|
|
|
2012-07-20 12:58:07 +02:00
|
|
|
# we are in fact just overwriting an eventually
|
|
|
|
|
# existing field with a new one, instead of modifying
|
|
|
|
|
# it, so we are using the conversion automatism in
|
|
|
|
|
# Field::new()
|
|
|
|
|
$this->addfield($field);
|
|
|
|
|
|
2012-07-20 13:40:43 +02:00
|
|
|
# mark the field as modified if it's passwd field
|
2015-02-16 11:41:35 +01:00
|
|
|
$this->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => 'mtime',
|
|
|
|
|
value => $time
|
|
|
|
|
)) if $name eq 'passwd';
|
|
|
|
|
|
|
|
|
|
$this->addfield(Crypt::PWSafe3::Field->new(
|
|
|
|
|
name => "lastmod",
|
|
|
|
|
value => $time
|
|
|
|
|
));
|
2015-05-21 11:10:42 +02:00
|
|
|
|
|
|
|
|
my ($package, $filename, $line, $subroutine, @ignore) = caller(1);
|
|
|
|
|
|
|
|
|
|
# this looks a little bit weird but it's a cool feat.
|
|
|
|
|
# 'super' contains the vault object (of class Crypt::PWSafe3),
|
|
|
|
|
# which initially called our new() method, so we know to which
|
|
|
|
|
# vault we belong.
|
|
|
|
|
# therefore, if the user just calls $record->passwd('newpw'),
|
|
|
|
|
# then we can update the record directly on the vault object,
|
|
|
|
|
# so that the user doesn't have to call modifyrecord. this is
|
|
|
|
|
# especially usefull inside a loop.
|
|
|
|
|
# also note, that the 'super' parameter to Crypt::PWSafe3::Record::new()
|
|
|
|
|
# is not documented, so it's an internal parameter not to be used
|
|
|
|
|
# by users. however, maybe in the future it would be useful to
|
|
|
|
|
# have it populated so that if a user has a function which takes a
|
|
|
|
|
# record as parameter, then in this function he could access the
|
|
|
|
|
# vault as well. maybe.
|
|
|
|
|
#
|
|
|
|
|
# Thu May 21 10:04:15 CEST 2015 tlinden\@cpan.org
|
|
|
|
|
if (exists $this->{super} &&
|
|
|
|
|
"${package}::${subroutine}" !~ /Crypt::PWSafe3::modifyrecord$/ &&
|
|
|
|
|
"${package}::${subroutine}" !~ /Crypt::PWSafe3::newrecord$/ &&
|
|
|
|
|
"${package}::${subroutine}" !~ /Crypt::PWSafe3::Record::modifyfield$/
|
|
|
|
|
) {
|
|
|
|
|
# we've been called from the outside (the user in fact) and
|
|
|
|
|
# we're attached to a vault, so update ourselfes there as well
|
|
|
|
|
$this->{super}->modifyrecord($this->uuid, $name, $value);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 12:58:07 +02:00
|
|
|
return $field;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
croak "Unknown field $name";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub genuuid {
|
|
|
|
|
#
|
|
|
|
|
# generate a v4 uuid string
|
|
|
|
|
my($this) = @_;
|
2015-02-16 11:41:35 +01:00
|
|
|
my $ug = Data::UUID->new();
|
2012-07-20 12:58:07 +02:00
|
|
|
my $uuid = $ug->create();
|
|
|
|
|
return $uuid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub addfield {
|
|
|
|
|
#
|
|
|
|
|
# add a field to the record
|
|
|
|
|
my ($this, $field) = @_;
|
2012-11-18 00:38:10 +01:00
|
|
|
my $name = $map2name{$field->type};
|
|
|
|
|
unless( defined($name) ) {
|
|
|
|
|
$name = $field->type; # consistent with Field->new
|
|
|
|
|
}
|
|
|
|
|
$this->{field}->{ $name } = $field;
|
2012-07-20 12:58:07 +02:00
|
|
|
}
|
|
|
|
|
|
2013-07-05 15:26:34 +02:00
|
|
|
sub policy {
|
|
|
|
|
#
|
|
|
|
|
# return or set a password policy
|
|
|
|
|
my ($this, $policy) = @_;
|
|
|
|
|
|
|
|
|
|
if($policy) {
|
|
|
|
|
$this->{policy} = $policy;
|
|
|
|
|
$this->pwpol($policy->encode());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$this->{policy} = Crypt::PWSafe3::PasswordPolicy->new(raw => $this->pwpol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->{policy};
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-20 12:58:07 +02:00
|
|
|
=head1 NAME
|
|
|
|
|
|
|
|
|
|
Crypt::PWSafe3::Record - Represents a Passwordsafe v3 data record
|
|
|
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
|
|
|
|
|
use Crypt::PWSafe3;
|
|
|
|
|
my $record = $vault->getrecord($uuid);
|
|
|
|
|
$record->title('t2');
|
|
|
|
|
$record->passwd('foobar');
|
|
|
|
|
print $record->notes;
|
|
|
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
|
|
|
|
B<Crypt::PWSafe3::Record> represents a Passwordsafe v3 data record.
|
|
|
|
|
Each record consists of a number of fields of type B<Crypt::PWSafe3::Field>.
|
|
|
|
|
The class provides get/set methods to access the values of those
|
|
|
|
|
fields.
|
|
|
|
|
|
|
|
|
|
It is also possible to access the raw unencoded values of the fields
|
|
|
|
|
by accessing them directly, refer to L<Crypt::PWSafe3::Field> for more
|
|
|
|
|
details on this.
|
|
|
|
|
|
2015-05-21 11:10:42 +02:00
|
|
|
If the record object has been created by L<Crypt::PWSafe3> (and fetched with
|
|
|
|
|
Crypt::PWSafe3::getrecord), then it's still associated with the L<Crypt::PWSafe3>
|
|
|
|
|
parent object. Changes to the record will therefore automatically populated
|
|
|
|
|
back into the parent object (the vault). This is not the case if you created
|
|
|
|
|
the record object yourself.
|
|
|
|
|
|
2012-07-20 12:58:07 +02:00
|
|
|
=head1 METHODS
|
|
|
|
|
|
|
|
|
|
=head2 B<uuid([string])>
|
|
|
|
|
|
|
|
|
|
Returns the UUID without argument. Sets the UUID if an argument
|
|
|
|
|
is given. Must be a hex representation of an L<Data::UUID> object.
|
|
|
|
|
|
|
|
|
|
This will be generated automatically for new records, so you
|
|
|
|
|
normally don't have to cope with.
|
|
|
|
|
|
|
|
|
|
=head2 B<user([string])>
|
|
|
|
|
|
|
|
|
|
Returns the username without argument. Sets the username
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
=head2 B<title([string])>
|
|
|
|
|
|
|
|
|
|
Returns the title without argument. Sets the title
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
=head2 B<passwd([string])>
|
|
|
|
|
|
|
|
|
|
Returns the password without argument. Sets the password
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
=head2 B<notes([string])>
|
|
|
|
|
|
|
|
|
|
Returns the notes without argument. Sets the notes
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
=head2 B<group([string])>
|
|
|
|
|
|
|
|
|
|
Returns the group without argument. Sets the group
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
Group hierarchy can be done by separating subgroups
|
|
|
|
|
by dot, eg:
|
|
|
|
|
|
|
|
|
|
$record->group('accounts.banking');
|
|
|
|
|
|
|
|
|
|
=head2 B<ctime([time_t])>
|
|
|
|
|
|
|
|
|
|
Returns the creation time without argument. Sets the creation time
|
|
|
|
|
if an argument is given. Argument must be an integer timestamp
|
|
|
|
|
as returned by L<time()>.
|
|
|
|
|
|
|
|
|
|
This will be generated automatically for new records, so you
|
|
|
|
|
normally don't have to cope with.
|
|
|
|
|
|
|
|
|
|
=head2 B<atime([time_t])>
|
|
|
|
|
|
|
|
|
|
Returns the access time without argument. Sets the access time
|
|
|
|
|
if an argument is given. Argument must be an integer timestamp
|
|
|
|
|
as returned by L<time()>.
|
|
|
|
|
|
|
|
|
|
B<Crypt::PWSafe3> doesn't update the atime field currently. So if
|
|
|
|
|
you mind, do it yourself.
|
|
|
|
|
|
|
|
|
|
=head2 B<mtime([time_t])>
|
|
|
|
|
|
2012-07-20 13:40:43 +02:00
|
|
|
Returns the modification time of the passwd field without argument. Sets the modification time
|
2012-07-20 12:58:07 +02:00
|
|
|
if an argument is given. Argument must be an integer timestamp
|
|
|
|
|
as returned by L<time()>.
|
|
|
|
|
|
2012-07-20 13:40:43 +02:00
|
|
|
This will be generated automatically for modified records if the passwd field changed, so you
|
2012-07-20 12:58:07 +02:00
|
|
|
normally don't have to cope with.
|
|
|
|
|
|
|
|
|
|
=head2 B<lastmod([string])>
|
|
|
|
|
|
|
|
|
|
Returns the modification time without argument. Sets the modification time
|
|
|
|
|
if an argument is given. Argument must be an integer timestamp
|
|
|
|
|
as returned by L<time()>.
|
|
|
|
|
|
|
|
|
|
This will be generated automatically for modified records, so you
|
|
|
|
|
normally don't have to cope with.
|
|
|
|
|
|
|
|
|
|
=head2 B<url([string])>
|
|
|
|
|
|
|
|
|
|
Returns the url without argument. Sets the url
|
|
|
|
|
if an argument is given. The url must be in the well
|
|
|
|
|
known notation as:
|
|
|
|
|
|
|
|
|
|
proto://host/path
|
|
|
|
|
|
|
|
|
|
=head2 B<pwhist([string])>
|
|
|
|
|
|
|
|
|
|
Returns the password history without argument. Sets the password history
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
B<Crypt::PWSafe3> doesn't update the pwhist field currently. So if
|
|
|
|
|
you mind, do it yourself. Refer to L<Crypt::PWSafe3::Databaseformat>
|
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
|
|
=head2 B<pwpol([string])>
|
|
|
|
|
|
|
|
|
|
Returns the password policy without argument. Sets the password policy
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
2013-07-05 15:26:34 +02:00
|
|
|
This is the raw encoded policy string. If you want to access it, use the
|
|
|
|
|
B<policy()> method, see below.
|
|
|
|
|
|
|
|
|
|
=head2 B<policy([Crypt::PWSafe3::PasswordPolicy object])>
|
|
|
|
|
|
|
|
|
|
If called without arguments, returns a Crypt::PWSafe3::PasswordPolicy
|
|
|
|
|
object. See L<Crypt::PWSafe3::PasswordPolicy> for details, how to access
|
|
|
|
|
it.
|
|
|
|
|
|
|
|
|
|
To modify the password policy, create new Crypt::PWSafe3::PasswordPolicy
|
|
|
|
|
object or modify the existing one and pass it as argument to the
|
|
|
|
|
B<policy> method.
|
2012-07-20 12:58:07 +02:00
|
|
|
|
|
|
|
|
=head2 B<pwexp([string])>
|
|
|
|
|
|
|
|
|
|
Returns the password expire time without argument. Sets the password expire time
|
|
|
|
|
if an argument is given.
|
|
|
|
|
|
|
|
|
|
B<Crypt::PWSafe3> doesn't update the pwexp field currently. So if
|
|
|
|
|
you mind, do it yourself. Refer to L<Crypt::PWSafe3::Databaseformat>
|
|
|
|
|
for more details.
|
|
|
|
|
|
|
|
|
|
=head1 MANDATORY FIELDS
|
|
|
|
|
|
|
|
|
|
B<Crypt::PWSafe3::Record> creates the following fields automatically
|
|
|
|
|
on creation, because those fields are mandatory:
|
|
|
|
|
|
|
|
|
|
B<uuid> will be generated using L<Data::UUID>.
|
|
|
|
|
|
|
|
|
|
B<user, password, title> will be set to the empty string.
|
|
|
|
|
|
|
|
|
|
B<ctime, atime, mtime, lastmod> will be set to current
|
|
|
|
|
time of creation time.
|
|
|
|
|
|
|
|
|
|
=head1 SEE ALSO
|
|
|
|
|
|
|
|
|
|
L<Crypt::PWSafe3>
|
|
|
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
|
|
2015-02-12 15:18:18 +01:00
|
|
|
T.v.Dein <tlinden@cpan.org>
|
2012-07-20 12:58:07 +02:00
|
|
|
|
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
|
|
2015-02-12 15:18:18 +01:00
|
|
|
Copyright (c) 2011-2015 by T.v.Dein <tlinden@cpan.org>.
|
2012-07-20 12:58:07 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
=head1 LICENSE
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it
|
2015-02-12 15:18:18 +01:00
|
|
|
and/or modify it under the same terms of the Artistic
|
|
|
|
|
License 2.0, see: L<http://www.perlfoundation.org/artistic_license_2_0>
|
2012-07-20 12:58:07 +02:00
|
|
|
|
|
|
|
|
=cut
|
|
|
|
|
|
|
|
|
|
1;
|