diff --git a/CHANGELOG b/CHANGELOG index 6871c37..8423700 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +NEXT: Records created by Crypt::PWSafe3 (eg. the ones fetched + with getrecords) are now associated with the parent + object, so that you can modify them directly and call + $vault->save afterwards without using $vault->modifyrecord. + 1.21: forgot to load File::Spec diff --git a/lib/Crypt/PWSafe3.pm b/lib/Crypt/PWSafe3.pm index 075a8cf..1bab08b 100644 --- a/lib/Crypt/PWSafe3.pm +++ b/lib/Crypt/PWSafe3.pm @@ -31,7 +31,7 @@ use Data::Dumper; use Exporter (); use vars qw(@ISA @EXPORT); -$Crypt::PWSafe3::VERSION = '1.21'; +$Crypt::PWSafe3::VERSION = '1.22'; use Crypt::PWSafe3::Field; use Crypt::PWSafe3::HeaderField; @@ -281,7 +281,7 @@ sub read { } # read db records - my $record = Crypt::PWSafe3::Record->new(); + my $record = Crypt::PWSafe3::Record->new(super => $this); $this->{record} = {}; while (1) { my $field = $this->readfield(); @@ -290,7 +290,7 @@ sub read { } if ($field->type == 0xff) { $this->addrecord($record); - $record = Crypt::PWSafe3::Record->new(); + $record = Crypt::PWSafe3::Record->new(super => $this); } else { $record->addfield($field); @@ -589,6 +589,7 @@ sub newrecord { } $this->markmodified(); $this->addrecord($record); + $this->{records}->{$record->uuid}->{super} = $this; return $record->uuid; } @@ -859,16 +860,21 @@ which is a unique identifier. You can access the uuid by: Accessing other record properties works the same. For more details, refer to L. -Please note that record objects accessed this way are -copies. If you change such a record object and save the -database, nothing will in fact change. In this case you -need to put the changed record back into the record -list of the Crypt::PWSafe3 object by: +B: record objects returned by getrecords() are +still associated with the L object. So, +if you modify a field of such a record, the change will +be populated back into the vault. Of course you'd still +need to save it. - $vault->addrecord($record): +Sample: -See section L for more details on this. + foreach my $rec ($vault->getrecords) { + $rec->note('blah fasel'); + } + $vault->save(); +However, it's also possible to use the B +method, see below. =head2 B @@ -998,7 +1004,7 @@ License 2.0, see: L =head1 VERSION -Crypt::PWSafe3 Version 1.21. +Crypt::PWSafe3 Version 1.22. =cut diff --git a/lib/Crypt/PWSafe3/Record.pm b/lib/Crypt/PWSafe3/Record.pm index 4bcfcf3..3b2f217 100644 --- a/lib/Crypt/PWSafe3/Record.pm +++ b/lib/Crypt/PWSafe3/Record.pm @@ -15,7 +15,7 @@ my %map2type = %Crypt::PWSafe3::Field::map2type; my %map2name = %Crypt::PWSafe3::Field::map2name; -$Crypt::PWSafe3::Record::VERSION = '1.09'; +$Crypt::PWSafe3::Record::VERSION = '1.10'; foreach my $field (keys %map2type ) { eval qq( @@ -34,9 +34,9 @@ foreach my $field (keys %map2type ) { sub new { # # new record object - my($this) = @_; + my($this, %param) = @_; my $class = ref($this) || $this; - my $self = { }; + my $self = \%param; bless($self, $class); $self->{field} = (); @@ -123,6 +123,35 @@ sub modifyfield { name => "lastmod", value => $time )); + + 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); + } + return $field; } else { @@ -189,6 +218,12 @@ It is also possible to access the raw unencoded values of the fields by accessing them directly, refer to L for more details on this. +If the record object has been created by L (and fetched with +Crypt::PWSafe3::getrecord), then it's still associated with the L +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. + =head1 METHODS =head2 B diff --git a/sample/test.pl b/sample/test.pl index 9e06a25..6b459ea 100755 --- a/sample/test.pl +++ b/sample/test.pl @@ -41,7 +41,8 @@ Passwd: %s Notes: %s ), $rec->uuid, $rec->user, $rec->passwd, $rec->group, $rec->title, $rec->notes; - $vault->modifyrecord($rec->uuid, notes => scalar localtime(time)); + $rec->notes( scalar localtime(time)); +# $vault->modifyrecord($rec->uuid, notes => scalar localtime(time)); } $vault->save; diff --git a/t/run.t b/t/run.t index a7d5b82..bac8541 100644 --- a/t/run.t +++ b/t/run.t @@ -12,7 +12,7 @@ # use Data::Dumper; -use Test::More tests => 11; +use Test::More tests => 13; #use Test::More qw(no_plan); @@ -39,17 +39,19 @@ sub rdpw { BEGIN { use_ok "Crypt::PWSafe3"}; require_ok( 'Crypt::PWSafe3' ); -# I'm going to replace the secure random number generator -# backends with this very primitive and insecure one, because -# these are only unit tests and because we use external modules -# for the purpose anyway (which are not to be tested with these -# unit tests). -# This has to be done, so that unit tests running on cpantesters -# don't block if we use a real random source, which has reportedly -# happened in the past. -# ***** CAUTION: DO NOT USE THIS CODE IN PRODUCTION. EVER. **** -*Crypt::PWSafe3::random = sub { return join'',map{chr(int(rand(255)))}(1..$_[1]); }; - +{ + # I'm going to replace the secure random number generator + # backends with this very primitive and insecure one, because + # these are only unit tests and because we use external modules + # for the purpose anyway (which are not to be tested with these + # unit tests). + # This has to be done so that unit tests running on cpantesters + # don't block if we use a real (and exhausted) random source, + # which has reportedly happened in the past. + # ***** CAUTION: DO NOT USE THIS CODE IN PRODUCTION. EVER. **** + no warnings 'redefine'; + *Crypt::PWSafe3::random = sub { return join'',map{chr(int(rand(255)))}(1..$_[1]); }; +}; ### 2 # open vault and read in all records @@ -77,7 +79,7 @@ my $tmpfile = "$fd"; close($fd); eval { - my $vault = Crypt::PWSafe3->new(file => $tmpfile, password => 'tom', random => $trand) or die "$!"; + my $vault = Crypt::PWSafe3->new(file => $tmpfile, password => 'tom') or die "$!"; $vault->newrecord(%record); $vault->save(); }; @@ -116,8 +118,30 @@ eval { $rdata3{$name} = $rec3->$name(); } }; -ok(!$@, "read a pwsafe3 database and change a record ($@)"); -is_deeply(\%record, \%rdata3, "Change a record an check if changes persist after saving"); +ok(!$@, "read a pwsafe3 database and change a record, traditional method ($@)"); +is_deeply(\%record, \%rdata3, "Change a record an check if changes persist after saving, traditional method"); +diag("3 done\n"); + +### 3a +# modify an existing record, new method +my $uuid3a; +my %rdata3a; +my $rec3a; + +eval { + my $vault3a = &rdpw('t/tom.psafe3'); + foreach my $rec ($vault3a->getrecords) { + $rec->notes('n3a'); + $uuid3a = $rec->uuid; + last; + } + $vault3a->save(file=>'t/3a.out'); + + my $rvault3a = &rdpw('t/3a.out'); + $rec3a = $rvault3a->getrecord($uuid3a); +}; +ok(!$@, "read a pwsafe3 database and change a record, new method ($@)"); +is_deeply($rec3a->notes, 'n3a', "Change a record an check if changes persist after saving, new method"); ### 4