File Coverage

blib/lib/Net/LDAP/Batch/Action/Update.pm
Criterion Covered Total %
statement 49 84 58.3
branch 15 48 31.2
condition 6 27 22.2
subroutine 6 7 85.7
pod 3 3 100.0
total 79 169 46.7


line stmt bran cond sub pod time code
1             package Net::LDAP::Batch::Action::Update;
2 3     3   18 use strict;
  3         8  
  3         97  
3 3     3   15 use warnings;
  3         6  
  3         75  
4 3     3   13 use Carp;
  3         8  
  3         196  
5 3     3   306 use base qw( Net::LDAP::Batch::Action );
  3         14  
  3         14487  
6              
7             our $VERSION = '0.02';
8              
9             __PACKAGE__->mk_accessors(qw( before search replace delete dn prev_dn ));
10              
11             =head1 NAME
12              
13             Net::LDAP::Batch::Action::Update - update entry on LDAP server
14              
15             =head1 SYNOPSIS
16              
17             use Net::LDAP::Batch::Action::Update;
18             my $action = Net::LDAP::Batch::Action::Update->new(
19             {
20             ldap => $net_ldap_object,
21             search => [
22             base => 'name=foo,dc=company,dc=com'
23             scope => 'base'
24             ],
25             replace => {
26             mail => 'bar@company.com'
27             },
28             delete => {
29             someAttr => ['val1', 'val2'],
30             },
31             });
32             $action->execute or $action->rollback;
33            
34              
35             =head1 DESCRIPTION
36              
37             Updates an entry from a LDAP server, restoring it on failure of any kind.
38              
39             =head2 init
40              
41             Override base method to check that search() param is set to an array ref.
42              
43             =cut
44              
45             sub init {
46 1     1 1 2 my $self = shift;
47 1         20 $self->SUPER::init(@_);
48 1 50       14 if ( !$self->entry ) {
49 1 50 33     17 if ( !$self->search
      33        
50             or !ref( $self->search )
51             or ref( $self->search ) ne 'ARRAY' )
52             {
53 0         0 croak "search array ref required";
54             }
55             }
56 1         33 return $self;
57             }
58              
59             =head2 execute
60              
61             If entry() is set, will simply call update() on the Net::LDAP::Entry
62             and croak on any error.
63              
64             Otherwise, uses search(), replace() and (optionally) delete() to
65             instatiate a Net::LDAP::Entry object, alter its attributes and write
66             it back to the LDAP server.
67              
68             =cut
69              
70             sub execute {
71 1     1 1 4 my $self = shift;
72              
73 1         3 my $entry;
74 1 50       4 if ( $self->entry ) {
75 0         0 $entry = $self->entry;
76             }
77             else {
78 1 50       11 my $search = $self->search or croak "search required";
79 1         34 my $where = Data::Dump::dump($search);
80              
81 1 50       657 carp "updating $where" if $self->debug;
82              
83 1 50 33     13 if ( $self->dn && ref( $self->dn ) ne 'HASH' ) {
84 0         0 croak "dn() must be a hash ref";
85             }
86 1 50 33     12 if ( $self->replace && ref( $self->replace ) ne 'HASH' ) {
87 0         0 croak "replace() must be a hash ref";
88             }
89 1 50 33     31 if ( $self->delete and ref( $self->delete ) ne 'HASH' ) {
90 0         0 croak "delete() must be a hash ref";
91             }
92              
93 1 0 33     34 if ( !$self->replace and !$self->dn and !$self->delete ) {
      0        
94 0         0 croak "nothing to update for $where";
95             }
96              
97 1         11 my $msg = $self->ldap->search(@$search);
98 1 50       46598 if ( $msg->count > 0 ) {
99 1         21 $entry = $msg->entry(0);
100 1         44 $self->before( $entry->clone ); # before
101 1 50       216 if ( $self->replace ) {
102 1         11 my $replace = $self->replace;
103 1         15 for my $key ( sort keys %$replace ) {
104 1         3 my $new = $replace->{$key};
105 1         69 my @old = $entry->get_value($key);
106 1 50       18 carp "updating $key from "
107             . Data::Dump::dump( \@old ) . " -> "
108             . Data::Dump::dump($new)
109             if $self->debug;
110 1         16 $entry->replace( $key => $new );
111             }
112             }
113 1 50       36 if ( $self->delete ) {
114 1         7 for my $key ( sort keys %{ $self->delete } ) {
  1         3  
115 1 50       7 carp "deleting $key from entry" if $self->debug;
116 1         71 $entry->delete( $key => $self->delete->{$key} );
117             }
118             }
119 1 50       380 if ( $self->dn ) {
120              
121 0         0 $self->prev_dn( $entry->dn );
122              
123 0 0       0 if ( $self->debug ) {
124 0         0 carp "changing dn from "
125             . $self->prev_dn . " to "
126             . Data::Dump::dump( $self->dn );
127             }
128              
129 0         0 $entry->changetype('moddn');
130 0         0 for my $attr ( keys %{ $self->dn } ) {
  0         0  
131 0         0 $entry->replace( $attr => $self->dn->{$attr} );
132             }
133             }
134 1         16 $self->entry($entry); # after
135             }
136             else {
137              
138             # no match for search.
139             # in SQL, this would just be a no-op, since WHERE failed.
140             # but here we assume that caller expects the object to exist.
141 0         0 croak "update search for $where failed to match";
142              
143             }
144             }
145              
146 1         21 my $msg = $entry->update( $self->ldap );
147 1 50       7401 if ( $msg->code ) {
148 0         0 croak "failed to update entry: " . $self->get_ldap_err($msg);
149             }
150              
151 1         86 $self->complete(1);
152 1         38 return 1;
153             }
154              
155             =head2 rollback
156              
157             Cannot rollback an entry if you did not specify a search() and replace()
158             value (i.e., if you set entry() explicitly prior to execute).
159              
160             =cut
161              
162             sub rollback {
163 0     0 1   my $self = shift;
164 0 0         return 0 unless $self->complete;
165              
166 0           my $before = $self->before;
167 0 0 0       if ( !$before or !$before->isa('Net::LDAP::Entry') ) {
168 0           croak "no original Net::LDAP::Entry to rollback to in update";
169             }
170              
171 0           my $after = $self->entry;
172 0 0 0       if ( !$after or !$after->isa('Net::LDAP::Entry') ) {
173 0           croak "no updated Net::LDAP::Entry to revert";
174             }
175              
176 0 0         my $search = $self->search or croak "search required";
177 0 0         my $replace = $self->replace or croak "replace required";
178 0           my $where = Data::Dump::dump($search);
179              
180 0 0         carp "rollback update for $where" if $self->debug;
181              
182             # put the old values back.
183 0           for my $key ( sort keys %$replace ) {
184 0           my $old = $before->get_value($key);
185 0           $after->replace( $key => $old );
186             }
187              
188             # revert any DN changes -- TODO test this!!
189             #if ( $self->dn ) {
190             # $after->dn( $self->prev_dn );
191             # $after->changetype('moddn');
192             # for my $attr ( keys %{ $self->dn } ) {
193             # $after->replace( $attr => $self->dn->{$attr} );
194             # }
195             # }
196              
197             # save the old values
198 0           my $msg = $after->update( $self->ldap );
199 0 0         if ( $msg->code ) {
200 0           croak "failed to rollback $where: " . $self->get_ldap_err($msg);
201             }
202              
203 0           $self->complete(0);
204 0           return 1;
205             }
206              
207             1;
208              
209             __END__