source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql.pm @ 2210

Last change on this file since 2210 was 2210, checked in by nanardon, 5 years ago

Fix function return

  • Property svn:keywords set to Id Rev
File size: 29.0 KB
Line 
1package LATMOS::Accounts::Bases::Sql;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases);
8use LATMOS::Accounts::Log;
9use DBI;
10use Crypt::RSA;
11use Crypt::RSA::Key::Public::SSH;
12use Crypt::RSA::Key::Private::SSH;
13use Crypt::RSA::Key::Public;
14use Crypt::RSA::Key::Private;
15use Crypt::Blowfish;
16use MIME::Base64;
17
18our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
19
20sub SCHEMA_VERSION { 29 };
21
22=head1 NAME
23
24LATMOS::Ad - Perl extension for blah blah blah
25
26=head1 SYNOPSIS
27
28  use LATMOS::Accounts::Bases;
29  my $base = LATMOS::Accounts::Bases->new('unix');
30  ...
31
32=head1 DESCRIPTION
33
34Account base access over standard unix file format.
35
36=head1 FUNCTIONS
37
38=cut
39
40=head2 SCHEMA_VERSION
41
42Return the SQL schema version to use for this software version.
43
44=head2 new(%config)
45
46Create a new LATMOS::Ad object for windows AD $domain.
47
48domain / server: either the Ad domain or directly the server
49
50ldap_args is an optionnal list of arguments to pass to L<Net::LDAP>.
51
52=cut
53
54sub new {
55    my ($class, %config) = @_;
56   
57    my $base = {
58        db_conn => $config{db_conn},
59    };
60
61    bless($base, $class);
62}
63
64sub DESTROY {
65    my ($self) = @_;
66    $self->{_db} && $self->{_db}->rollback;
67}
68
69=head2 db
70
71Return a L<DBI> handle over database, load it if need.
72
73=cut
74
75sub db {
76    my ($self) = @_;
77
78    if ($self->{_db} && $self->{_db}->ping) {
79        return $self->{_db};
80    } else {
81        $self->{_db} = DBI->connect_cached(
82            'dbi:Pg:' . $self->{db_conn},
83            undef, undef,
84            {
85                RaiseError => 0,
86                AutoCommit => 0,
87                PrintWarn => 1,
88                PrintError => 1,
89            }
90        ) or do {
91            $self->log(LA_ERR, "Cannot connect to database: %s", $DBI::errstr);   
92            return;
93        };
94        $self->{_db}->do(q{set DATESTYLE to 'DMY'});
95        $self->log(LA_DEBUG, 'New connection to DB');
96
97        {
98            my $sv = $self->get_global_value('schema_version') || 1;
99            if ($sv < SCHEMA_VERSION) {
100                require LATMOS::Accounts::Bases::Sql::upgrade;
101                if ($self->SchemaUpgrade()) {
102                    $self->commit;
103                } else {
104                    $self->rollback;
105                    return;
106                }
107            }
108        }
109
110        foreach my $otype ($self->list_supported_objects) {
111            my %attrlist = map { $_ => 1 } $self->list_registered_attributes($otype);
112            foreach my $attribute ($self->list_canonical_fields($otype, 'a')) {
113                my $attr = $self->attribute($otype, $attribute);
114                $attr->{inline} and next;
115                $attr->{managed} and next;
116
117                if ($attrlist{$attribute}) {
118                } else {
119                    if($self->register_attribute($otype, $attribute, $attr->{comment})) {
120                        $self->log(LA_NOTICE, "Attr. $attribute for object type $otype registred");
121                    } else {
122                        $self->log(LA_ERR, "Can't register attribute $attribute");
123                        $self->{_db}->rollback;
124                        return;
125                    }
126                }
127            }
128        }
129
130        $self->{_db}->commit;
131        return $self->{_db};
132    }
133}
134
135=head2 load
136
137Read file and load data into memory
138
139=cut
140
141sub load {
142    my ($self) = @_;
143    if (!$self->db) { return 0 };
144
145    my $sv = $self->get_global_value('schema_version') || 1;
146    if ($sv < SCHEMA_VERSION) {
147        $self->log(LA_CRIT,
148            "Schema version %d found, %d is need, please update db using " .
149            "`la-sql-upgrade' tool for `%s' base",
150            $sv,
151            SCHEMA_VERSION,
152            $self->label,
153        );
154        # return;
155    }
156    if (!$self->_CreateInternalObjects) {
157        $self->rollback;
158    }
159
160    1;
161}
162
163sub _CreateInternalObjects {
164    my ($self) = @_;
165
166    my $dbi = $self->{_db};
167
168    my @objects = (
169        {
170            name => 'dpmt',
171            otype => 'sutype',
172            attrs => {
173                description => 'Department',
174            },
175        },
176        {
177            name => 'contrattype',
178            otype => 'sutype',
179            attrs => {
180                description => 'Contract',
181            },
182        },
183        {
184            name => '-useralias',
185            otype => 'group',
186            attrs => {
187                description => 'Internal group for user alias object',
188                gidnumber => -1,
189                unexported => 1,
190            },
191            intern => 1,
192        },
193    );
194
195    my $change = 0;
196
197    $self->temp_switch_unexported( sub {
198
199            my $setnodel  = $dbi->prepare('UPDATE objects SET nodelete     = true where name = ? and nodelete     = false');
200            my $setintern = $dbi->prepare('UPDATE objects SET internobject = true where name = ? and internobject = false');
201
202            foreach (@objects) {
203                if (!$self->GetRawObject($_->{otype}, $_->{name})) {
204                    printf("Creating object %s/%s\n", $_->{otype}, $_->{name});
205                    $self->create_object($_->{otype}, $_->{name}, %{$_->{attrs} || {}})
206                        or warn sprintf("cannot create %s/%s\n", $_->{otype}, $_->{name});
207            $change += 1;
208                }
209
210                $change +=  $setnodel->execute($_->{name});
211            $change +=  $setintern->execute($_->{name}) if ($_->{intern});
212            }
213
214    }, 1);
215
216    if ($change > 0) {
217        $self->db->commit;
218    }
219
220    return 1;
221}
222
223=head2 ListInternalObjects($otype)
224
225List objects flags as internal for type C<$otype>
226
227=cut
228
229sub ListInternalObjects {
230    my ($self, $otype) = @_;
231
232    my $pclass = $self->_load_obj_class($otype) or return;
233
234    # Object Alias: checking if object is alias, then returning it:
235    my $sth = $self->db->prepare_cached(
236        sprintf(q{select %s as k from %s where and internobject = true},
237            $self->db->quote_identifier($pclass->_key_field),
238            $self->db->quote_identifier($pclass->_object_table),
239        ),
240    );
241    $sth->execute();
242    my @list;
243    while (my $res = $sth->fetchrow_hashref) {
244       push(@list, $_->{k});
245   }
246   return(@list);
247}
248
249=head2 GetRawObject($otype, $id)
250
251Return an object even it is internal, alias are not follow and even
252unexported object are returned
253
254This function must be used only for maintenance operation.
255
256=cut
257
258sub GetRawObject {
259    my ($self, $otype, $id) = @_;
260
261    my $pclass = $self->_load_obj_class($otype) or return;
262
263    return $self->SUPER::get_object($otype, $id);
264}
265
266sub _derefObject {
267    my ($self, $otype, $oalias) = @_;
268    $oalias or return;
269    if (my ($aliasotype, $aliasoname, $aliasattr) = $oalias =~ m/^([^\/]+)\.([^\.]+)\.(.*)$/) {
270        my $attribute = $self->attribute($aliasotype, $aliasattr) or do {
271            $self->log(LA_DEBUG, "Oalias %s (%s): can fetch attibute %s/%s",
272                $otype, $oalias, $aliasotype, $aliasattr);
273            return;
274        };
275        my $refotype = $attribute->reference or do {
276            $self->log(LA_DEBUG, "Oalias %s (%s): Attribute does not reference an object",
277                $otype, $oalias);
278            return;
279        };
280        $refotype eq $otype or do {
281            $self->log(LA_DEBUG, "Oalias %s (%s): Attribute does not reference same object type",
282                $otype, $oalias, $refotype);
283            return;
284        };
285        my $robj = $self->get_object($aliasotype, $aliasoname) or do {
286            $self->log(LA_DEBUG, "Oalias %s (%s): can fetch object %s/%s",
287                $otype, $oalias, $aliasotype, $aliasoname);
288            return;
289        };
290        my $rvalue = $robj->get_attributes($aliasattr) or do {
291            $self->log(LA_DEBUG, "Oalias %s (%s): attribute value is empty",
292                $otype, $oalias);
293            return;
294        };
295        return $self->get_object($refotype, $rvalue);
296    } else {
297        return $self->get_object($otype, $oalias);
298    }
299}
300
301sub get_object {
302    my ($self, $otype, $id) = @_;
303
304    my $pclass = $self->_load_obj_class($otype) or return;
305
306    # Object Alias: checking if object is alias, then returning it:
307    my $sth = $self->db->prepare_cached(
308        sprintf(q{select oalias from %s where %s = ? and internobject = false %s},
309            $self->db->quote_identifier($pclass->_object_table),
310            $self->db->quote_identifier($pclass->_key_field),
311            ($self->{wexported} ? '' : 'and exported = true'),
312        ),
313    );
314    $sth->execute($id);
315    my $res = $sth->fetchrow_hashref;
316    $sth->finish;
317    if (my $oalias = $res->{oalias}) {
318        # Cross reference over object/attribute
319        $self->_derefObject($otype, $oalias);
320    } else {
321        return $self->SUPER::get_object($otype, $id);
322    }
323}
324
325=head2 getObjectFromOKey ($okey)
326
327Return the object from the db internal key
328
329=cut
330
331sub getObjectFromOKey {
332    my ($self, $okey) = @_;
333
334    my $findobj = $self->{_db}->prepare_cached(q{
335        select * from objects_table where ikey = ?
336    });
337
338    $findobj->execute($okey);
339
340    my $res = $findobj->fetchrow_hashref;
341    $findobj->finish;
342
343    if ($res) {
344        return $self->get_object($res->{relname}, $res->{name});
345    } else {
346        return;
347    }
348}
349
350sub _sync_dyn_group {
351    my ($self) = @_;
352
353    my @groups = $self->search_objects('group', 'autoMemberFilter=*');
354
355    my $res = 0;
356    foreach (@groups) {
357        my $g = $self->get_object('group', $_) or next;
358        $res += $g->populate_dyn_group;
359    }
360
361    $self->log(LA_DEBUG, "Group Dyn res %d", $res);
362    $res
363}
364
365sub _sync_dyn_aliases {
366    my ($self) = @_;
367
368    my @groups = $self->search_objects('aliases', 'autoMemberFilter=*');
369
370    my $res = 0;
371    foreach (@groups) {
372        my $g = $self->get_object('aliases', $_) or next;
373        $res += $g->populate_dyn_aliases;
374    }
375
376    $self->log(LA_DEBUG, "Aliases Dyn res %d", $res);
377    $res
378}
379
380=head2 PopulateDynData
381
382Recomputate dynamics attributes (autoMembersFilters) if need
383
384=cut
385
386sub PopulateDynData {
387    my ($self) = @_;
388
389    $self->log(LA_DEBUG, 'Running PopulateDynData()');
390
391    $self->temp_switch_unexported(sub {
392
393        foreach (1 .. 5) {
394            $self->log(LA_DEBUG, "%d loop for PopulateDynData", $_);
395            my $res = 0;
396            $res += $self->_sync_dyn_group   || 0;
397            $res += $self->_sync_dyn_aliases || 0;
398
399            if ($res == 0) {
400                last;
401            }
402        }
403
404        my %sshUser = map { $_ => 1 } (
405            $self->search_objects('user', 'authorizedKeys=*', 'oalias=NULL'),
406            $self->search_objects('user', 'sshPublicKeyFilter=*', 'oalias=NULL'),
407            $self->search_objects('user', 'sshPublicKey=*', 'oalias=NULL'),
408        );
409
410        foreach my $user (keys %sshUser) {
411            my $ouser = $self->get_object('user', $user) or next;
412            $ouser->set_fields( 'authorizedKeys', $ouser->_get_c_field('_authorizedKeys') );
413        }
414
415    }, 0);
416
417    return 1;
418}
419
420sub _commit {
421    my ($self) = @_;
422
423    # Let sync-manager update data in background
424    $self->PopulateDynData unless($self->config('ASyncDynData'));
425
426    if ($ENV{LA_NO_COMMIT}) {
427        $self->log(LA_DEBUG, 'DB::COMMIT (ignore due to LA_NO_COMMIT)');
428        return 1;
429    } else {
430        $self->log(LA_DEBUG, 'DB::COMMIT');
431    }
432    $self->{__cache} = undef;
433    $self->db->commit;
434}
435
436sub _rollback {
437    my ($self) = @_;
438    if ($ENV{LA_NO_COMMIT}) {
439        $self->log(LA_DEBUG, 'DB::ROLLBACK (ignore due to LA_NO_COMMIT)');
440        return 1
441    } else {
442        $self->log(LA_DEBUG, 'DB::ROLLBACK');
443    }
444    $self->{__cache} = undef;
445    $self->db->rollback;
446}
447
448sub list_supported_objects {
449    my ($self, @otype) = @_;
450    $self->SUPER::list_supported_objects(qw(site), @otype);
451}
452
453# For SQL listRealObjects != list_objects
454sub listRealObjects {
455    my ($self, $otype) = @_;
456    my $pclass = $self->_load_obj_class($otype) or return;
457    $pclass->listReal($self);
458}
459
460sub current_rev {
461    my ($self) = @_;
462    my $sth = $self->db->prepare_cached(
463        q{select max(rev) from revisions}
464    );
465    $sth->execute;
466    my $res = $sth->fetchrow_hashref;
467    $sth->finish;
468    return ($res->{max});
469} 
470
471sub authenticate_user {
472    my ($self, $username, $passwd) = @_;
473    $username or return;
474    my $uobj = $self->get_object('user', $username) or do {
475        la_log(LA_ERR, "Cannot authenticate non existing SQL user $username");
476        return;
477    };
478
479    if ($self->attribute('user', 'exported')) {
480        if (!$uobj->_get_c_field('exported')) {
481            la_log(LA_ERR, "User $username found but currently unexported");
482            return;
483        }
484    }
485
486    $self->SUPER::authenticate_user($username, $passwd);
487}
488
489=head1 SPECIFICS FUNCTIONS
490
491=head2 GetAlias($base, $id)
492
493Return object having id C<$id> only if it is an object alias
494
495=cut
496
497sub GetAlias {
498    my ($self, $otype, $id) = @_;
499
500    my $pclass = $self->_load_obj_class($otype) or return;
501
502    # Object Alias: checking if object is alias, then returning it:
503    my $sth = $self->db->prepare_cached(
504        sprintf(q{select oalias from %s where %s = ? and oalias IS NOT NULL and internobject = false %s},
505            $self->db->quote_identifier($pclass->_object_table),
506            $self->db->quote_identifier($pclass->_key_field),
507            ($self->{wexported} ? '' : 'and exported = true'),
508        ),
509    );
510    $sth->execute($id);
511    my $res = $sth->fetchrow_hashref;
512    $sth->finish;
513    if ($res) {
514        return $self->SUPER::get_object($otype, $id);
515    } else {
516        return;
517    }
518}
519
520=head2 CreateAlias($otype, $name, $for)
521
522Create an object alias named C<$name> for ovbject C<$for>
523
524=cut
525
526sub CreateAlias {
527    my ($self, $otype, $name, $for) = @_;
528
529    my $pclass = $self->_load_obj_class($otype) or return;
530
531    $for or die "Cannot create alias without giving object to point";
532
533    my $res = $pclass->CreateAlias($self, $name, $for);
534
535    if ($res) {
536        $self->ReportChange(
537            $otype,
538            $name,
539            $pclass->_get_ikey($self, $name),
540            'Create', "Alias %s %s => %s", $otype, $name, $for
541        );
542        $self->log(LA_DEBUG, "Alias $otype $name => $for created");
543        my $oalias = $self->GetAlias($otype, $name);
544        $oalias->_update_aliases_ptr();
545        return 1;
546    } else {
547        $self->log(LA_ERR, "Error when creating alias $otype $name");
548        return;
549    }
550}
551
552=head2 RenameAlias($otype, $name, $to)
553
554Rename an object alias
555
556=cut
557
558sub RenameAlias {
559    my ($self, $otype, $name, $to) = @_;
560
561    my $pclass = $self->_load_obj_class($otype) or return;
562
563    my $obj = $self->GetAlias($otype, $name) or do {
564        $self->log('Cannot get alias %s/%s for removal', $otype, $name);
565        return;
566    };
567
568    my $sth = $self->db->prepare_cached(sprintf(
569        'UPDATE %s SET %s = ? WHERE %s = ?',
570        $self->db->quote_identifier($pclass->_key_field),
571        $self->db->quote_identifier($pclass->_object_table),
572        $self->db->quote_identifier($pclass->_key_field),
573    ));
574
575    my $res = $sth->execute($to, $name);
576
577    return $res;
578}
579
580=head2 RemoveAlias($otype, $name, $for)
581
582Create an object alias named C<$name> for ovbject C<$for>
583
584=cut
585
586sub RemoveAlias {
587    my ($self, $otype, $name) = @_;
588
589    my $pclass = $self->_load_obj_class($otype) or return;
590
591    my $obj = $self->GetAlias($otype, $name) or do {
592        $self->log('Cannot get alias %s/%s for removal', $otype, $name);
593        return;
594    };
595
596    if ($obj->_get_attributes('internobject')) {
597        # Cannot happend: internal are not fetchable
598        $self->log(LA_ERR,'Cannot delete %s/%s: is an internal object', $pclass->type, $name);
599        return;
600    }
601    if ($obj->_get_attributes('nodelete')) {
602        $self->log(LA_ERR,'Cannot delete %s/%s: is write protected', $pclass->type, $name);
603        return;
604    }
605
606    my $id = $obj->Iid;
607
608    my $sth = $self->db->prepare_cached(sprintf(
609        'DELETE FROM %s WHERE %s = ?',
610        $self->db->quote_identifier($pclass->_object_table),
611        $self->db->quote_identifier($pclass->_key_field),
612    ));
613
614    $obj->_update_aliases_ptr;
615    my $res = $sth->execute($name);
616
617    if ($res) {
618        $self->ReportChange(
619            $otype,
620            $name,
621            $id,
622            'Delete', "Alias %s %s deleted", $otype, $name
623        );
624        $self->log(LA_DEBUG, "Alias $otype $name removed");
625        return 1;
626    } else {
627        $self->log(LA_ERR, "Error when removing alias $otype $name");
628        return;
629    }
630}
631
632=head2 get_global_value ($varname)
633
634Return global value set into base
635
636=cut
637
638sub get_global_value {
639    my ($self, $varname) = @_;
640
641    my $sth = $self->db->prepare_cached(q{
642        select val from settings where varname = ?
643        });
644    $sth->execute($varname);
645    my $res = $sth->fetchrow_hashref;
646    $sth->finish;
647    $res->{val}
648}
649
650=head2 set_global_value ($varname, $value)
651
652Set global value.
653
654=cut
655
656sub set_global_value {
657    my ($self, $varname, $value) = @_;
658    my $sth = $self->db->prepare(q{
659        update settings set val = ? where varname = ?
660        });
661    $sth->execute($value, $varname) == 0 and do {
662        my $sth2 = $self->db->prepare(q{
663            insert into settings (val, varname) values (?,?)
664            });
665        $sth2->execute($value, $varname);
666    };
667}
668
669=head2 del_global_value ($varname)
670
671Delete global value from base
672
673=cut
674
675sub del_global_value {
676    my ($self, $varname) = @_;
677
678    my $sth = $self->db->prepare_cached(q{
679        delete from settings where varname = ?
680        });
681    return $sth->execute($varname);
682}
683
684=head2 generate_rsa_key ($password)
685
686Return public and private peer rsa keys
687
688=cut
689
690sub generate_rsa_key {
691    my ($self, $password) = @_;
692
693    my $rsa = new Crypt::RSA ES => 'PKCS1v15';
694    my ($public, $private) = $rsa->keygen (
695        Identity  => 'LATMOS-Accounts',
696        Size      => 2048,
697        Password  => $password,
698        Verbosity => 0,
699    ) or die $rsa->errstr(); # TODO avoid die
700    return ($public, $private);
701}
702
703=head2 private_key ($password)
704
705Load and return private rsa key
706
707=cut
708
709sub private_key {
710    my ($self, $password) = @_;
711    my $base = $self;
712    my $serialize = $base->get_global_value('rsa_private_key') or return;
713    my $string = decode_base64($serialize);
714    my $privkey = $string =~ /^SSH PRIVATE KEY FILE/
715        ? Crypt::RSA::Key::Private::SSH->new
716        : Crypt::RSA::Key::Private->new;
717    $privkey = $privkey->deserialize(
718        String => [ $string ],
719        Password => $password
720    );
721    $privkey->reveal( Password => $password );
722    $privkey;
723}
724
725=head2 get_rsa_password
726
727Return hash with peer username => encryptedPassword
728
729=cut
730
731sub get_rsa_password {
732    my ($self) = @_;
733    my $base = $self;
734    my $sth = $base->db->prepare(q{
735        select "name", value from "user" join user_attributes_base
736        on "user".ikey = user_attributes_base.okey
737        where user_attributes_base.attr = 'encryptedPassword'
738    });
739    $sth->execute;
740    my %users;
741    while (my $res = $sth->fetchrow_hashref) {
742        $users{$res->{name}} = $res->{value};
743    }
744    %users
745}
746
747=head2 store_rsa_key ($public, $private)
748
749Store public and private RSA key info data base
750
751=cut
752
753sub store_rsa_key {
754    my ($self, $public, $private) = @_;
755    my $base = $self;
756    $private->hide;
757    $base->set_global_value('rsa_private_key',
758        encode_base64($private->serialize));
759    $base->set_global_value('rsa_public_key',
760        $public->serialize);
761    return;
762}
763
764=head2 find_next_expire_users ($expire)
765
766Search user expiring in C<$expire> delay
767
768=cut
769
770sub find_next_expire_users {
771    my ($self, $expire) = @_;
772
773    my $sth= $self->db->prepare(q{
774        select name from "user" where
775            expire < now() + ?::interval
776            and expire > now()
777            and expire is not null
778            and internobject = false
779            } . ($self->{wexported} ? '' : 'and exported = true') . q{
780            order by expire
781        }
782    );
783    $sth->execute($expire || '1 month');
784    my @users;
785    while (my $res = $sth->fetchrow_hashref) {
786        push(@users, $res->{name});
787    }
788    @users
789}
790
791=head2 find_expired_users ($expire)
792
793Return list of user going to expires in C<$expire> delay
794
795=cut
796
797sub find_expired_users {
798    my ($self, $expire) = @_;
799
800    my $sth= $self->db->prepare(q{
801        select name from "user" where
802            expire < now() - ?::interval
803            and expire is not null
804            and internobject = false
805        } . ($self->{wexported} ? '' : 'and exported = true') . q{
806            order by expire
807        }
808    );
809    $sth->execute($expire || '1 second');
810    my @users;
811    while (my $res = $sth->fetchrow_hashref) {
812        push(@users, $res->{name});
813    }
814    @users
815}
816
817=head2 rename_nethost ($nethostname, $to, %config)
818
819Facility function to rename computer to new name
820
821=cut
822
823sub rename_nethost {
824    my ($self, $nethostname, $to, %config) = @_;
825    {
826        my $obj = $self->get_object('nethost', $nethostname) or do {
827            $self->log(LA_ERR, 'Unable to rename non exisant host %s', $nethostname);
828            return;
829        };
830        $obj->_delAttributeValue(cname => $to);
831    }
832    $self->rename_object('nethost', $nethostname, $to) or return;
833    if ($config{'addcname'}) {
834        my $obj = $self->get_object('nethost', $to);
835        $obj->_addAttributeValue(cname => $nethostname);
836    }
837    return 1;
838}
839
840=head2 nethost_exchange_ip ($ip1, $ip2)
841
842Exchange ip1 with ip2 in base
843
844=cut
845
846sub nethost_exchange_ip {
847    my ($self, $ip1, $ip2) = @_;
848    my ($obj1, $obj2);
849    if (my ($host1) = $self->search_objects('nethost', "ip=$ip1")) {
850        $obj1 = $self->get_object('nethost', $host1);
851    } else {
852        $self->log(LA_ERR, "Cannot find host having $ip1");
853        return;
854    }
855    if (my ($host2) = $self->search_objects('nethost', "ip=$ip2")) {
856        $obj2 = $self->get_object('nethost', $host2);
857    } else {
858        $self->log(LA_ERR, "Cannot find host having $ip2");
859        return;
860    }
861    if ($obj1->id eq $obj2->id) {
862        $self->log(LA_ERR, "Both ip belong to same host (%s)", $obj1->id);
863        return;
864    }
865
866    $self->log(LA_NOTICE, "Exchanging IP between %s and %s", $obj1->id, $obj2->id);
867    $obj1->delAttributeValue('ip', $ip1) ;
868    $obj2->delAttributeValue('ip', $ip2) ;
869    $obj1->addAttributeValue('ip', $ip2) ;
870    $obj2->addAttributeValue('ip', $ip1) ;
871    return 1;
872}
873
874=head1 ATTRIBUTES FUNCTIONS
875
876=cut
877
878sub obj_attr_allowed_values {
879    my ($self, $otype, $attr) = @_;
880    if (my @values = $self->SUPER::obj_attr_allowed_values("$otype.$attr", 'allowed')) {
881        return @values;
882    } else {
883        $self->ListAttrValue($otype, $attr);
884    }
885}
886
887=head2 ListAttrValue($otype, $attribute)
888
889List values allow for an attribute set into SQL database
890
891=cut
892
893sub ListAttrValue {
894    my ($self, $otype, $attr) = @_;
895
896    my @sqlvalues;
897    my $getAllow = $self->db->prepare_cached(q{
898        SELECT * FROM attributes_values WHERE otype = ? AND attributes = ?
899        ORDER BY "value"
900    });
901    $getAllow->execute($otype, $attr);
902    while (my $res = $getAllow->fetchrow_hashref) {
903        push(@sqlvalues, $res->{value});
904    }
905    return @sqlvalues;
906}
907
908=head2 AddAttrValue($otype, $attr, @values)
909
910Add given values to allowed attribute list
911
912=cut
913
914sub AddAttrValue {
915    my ($self, $otype, $attr, @values) = @_;
916   
917    my $addAllow = $self->db->prepare_cached(q{
918        INSERT INTO attributes_values (otype, attributes, "value") values (?,?,?)
919    });
920   
921    foreach my $value (@values) {
922        if ($addAllow->execute($otype, $attr, $value)) {
923        } else {
924            $self->rollback;
925            return;
926        }
927    }
928
929    return 1;
930}
931
932=head2 DelAttrValue
933
934Delete a
935
936=cut
937
938sub DelAttrValue {
939    my ($self, $otype, $attr, @values) = @_;
940
941    if (@values) {
942        my $delAllow = $self->db->prepare_cached(q{
943            DELETE FROM attributes_values WHERE otype = ? and attributes = ? and "value" = ?
944        });
945
946        foreach my $value (@values) {
947
948            if ($delAllow->execute($otype, $attr, $value)) {
949            } else {
950                $self->rollback;
951                return;
952            }
953
954        }
955        return 1;
956    } else {
957        my $delAllow = $self->db->prepare_cached(q{
958            DELETE FROM attributes_values WHERE otype = ? and attributes = ?
959        });
960        if ($delAllow->execute($otype, $attr)) {
961            return 1;
962        } else {
963            $self->rollback;
964            return;
965        }
966    }
967}
968
969=head2 register_attribute ($otype, $attribute, $comment)
970
971Register a new attribute in base
972
973=cut
974
975sub register_attribute {
976    my ($self, $otype, $attribute, $comment) = @_;
977    my $pclass = $self->_load_obj_class($otype) or return;
978    $pclass->register_attribute($self, $attribute, $comment);
979}
980
981=head2 is_registered_attribute ($otype, $attribute)
982
983Return true is attribute already exists
984
985=cut
986
987sub is_registered_attribute {
988    my ($self, $otype, $attribute) = @_;
989    my $pclass = $self->_load_obj_class($otype) or return;
990    $pclass->is_registered_attribute($self, $attribute);
991}
992
993=head2 list_registered_attributes ($otype)
994
995List all regiestered attribute
996
997=cut
998
999sub list_registered_attributes {
1000    my ($self, $otype) = @_;
1001    my $pclass = $self->_load_obj_class($otype) or return;
1002    if ($pclass->_has_extended_attributes) {
1003        return $pclass->list_registered_attributes($self);
1004    } else {
1005        return ();
1006    }
1007}
1008
1009=head2 get_attribute_comment ($otype, $attribute)
1010
1011Return the comment associated to attribute
1012
1013=cut
1014
1015sub get_attribute_comment {
1016    my ($self, $otype, $attribute) = @_;
1017    my $pclass = $self->_load_obj_class($otype) or return;
1018    $pclass->get_attribute_comment($self, $attribute);
1019}
1020
1021=head2 set_attribute_comment ($otype, $attribute, $comment)
1022
1023Set comment to attribute
1024
1025=cut
1026
1027sub set_attribute_comment {
1028    my ($self, $otype, $attribute, $comment) = @_;
1029    my $pclass = $self->_load_obj_class($otype) or return;
1030    $pclass->set_attribute_comment($self, $attribute, $comment);
1031}
1032
1033=head2 getEmploymentRange
1034
1035Return date range within employment can be found in database
1036
1037=cut
1038
1039sub getEmploymentRange {
1040    my ($self, @filters) = @_;
1041
1042    my ($min,$max);
1043
1044    if (@filters) {
1045        my @flist = $self->search_objects('employment', @filters);
1046        my $minSql = $self->db->prepare(q{
1047            SELECT min(firstday) as min FROM employment
1048            WHERE name = ANY (?)
1049        });
1050        $minSql->execute(\@flist);
1051        if (my $res = $minSql->fetchrow_hashref) {
1052            $min = $res->{min}
1053        }
1054        my $maxSql = $self->db->prepare(q{
1055            SELECT max(lastday) as max FROM employment
1056            WHERE name = ANY (?)
1057        });
1058        $maxSql->execute(\@flist);
1059        if (my $res = $maxSql->fetchrow_hashref) {
1060            $max = $res->{max}
1061        }
1062    } else {
1063        my $minSql = $self->db->prepare(q{
1064            SELECT min(firstday) as min FROM employment
1065        });
1066        $minSql->execute;
1067        if (my $res = $minSql->fetchrow_hashref) {
1068            $min = $res->{min}
1069        }
1070        my $maxSql = $self->db->prepare(q{
1071            SELECT max(lastday) as max FROM employment
1072        });
1073        $maxSql->execute;
1074        if (my $res = $maxSql->fetchrow_hashref) {
1075            $max = $res->{max}
1076        }
1077    }
1078
1079    return ($min,$max);
1080}
1081
1082sub ReportChange {
1083    my ($self, $otype, $name, $ref, $changetype, $message, @args) = @_;
1084
1085    my $sthmodifiedby = $self->db->prepare(q{
1086        UPDATE objects set modifiedby = ? where ikey = ?
1087    });
1088
1089    $sthmodifiedby->execute(
1090        $self->user || '@Console',
1091        $ref,
1092    );
1093
1094    my ($potype, $pname, $pikey);
1095
1096    if (my $obj = $self->get_object($otype, $name)) {
1097        if (my $parent = $obj->ParentObject) {
1098            $potype = $parent->type;
1099            $pname  = $parent->id;
1100            $pikey  = $parent->Iid; 
1101        }
1102    }
1103
1104    my $sth = $self->db->prepare(q{
1105        INSERT into objectslogs (ikey, irev, otype, name, changetype, username, message, parentotype, parentname, parentikey)
1106        VALUES (?,?,?,?,?,?,?,?,?,?)
1107        });
1108
1109    $sth->execute(
1110        $ref,
1111        $self->current_rev,
1112        $otype,
1113        $name,
1114        $changetype,
1115        $self->user || '@Console',
1116        sprintf($message, @args),
1117        $potype, $pname, $pikey,
1118    );
1119}
1120
1121=head2 getobjectlogs($otype, $name)
1122
1123Return logs for object type C<$otype> having C<$name>.
1124
1125=cut 
1126
1127sub getobjectlogs {
1128    my ($self, $otype, $name) = @_;
1129
1130    my $sth = $self->db->prepare(q{
1131        select ikey from objectslogs where
1132            (otype = $1 and name  = $2)
1133        group by ikey
1134    });
1135    $sth->execute($otype, $name);
1136    my @ids;
1137    while (my $res = $sth->fetchrow_hashref) {
1138        push(@ids, $res->{ikey});
1139    }
1140    @ids or return;
1141
1142    my $sth2 = $self->db->prepare(
1143        q{
1144            select * from objectslogs where ikey = ANY ($1)
1145                or parentikey = ANY ($1)
1146            order by logdate asc
1147        },
1148    );
1149
1150    $sth2->execute( \@ids );
1151    my @logs;
1152    while (my $res = $sth2->fetchrow_hashref) {
1153        push(@logs, $res);
1154    }
1155
1156    return @logs;
1157}
1158
1159=head2 getlogs
1160
1161Return logs for last year
1162
1163=cut
1164
1165sub getlogs {
1166    my ($self) = @_;
1167    my $sth2 = $self->db->prepare(
1168        q{
1169            select * from objectslogs
1170            where logdate > now() - '1 year'::interval
1171            order by logdate asc
1172        },
1173    );
1174
1175    $sth2->execute();
1176    my @logs;
1177    while (my $res = $sth2->fetchrow_hashref) {
1178        push(@logs, $res);
1179    }
1180
1181    return @logs;
1182}
1183
1184
11851;
1186
1187__END__
1188
1189=head1 SEE ALSO
1190
1191=head1 AUTHOR
1192
1193Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
1194
1195=head1 COPYRIGHT AND LICENSE
1196
1197Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
1198
1199This library is free software; you can redistribute it and/or modify
1200it under the same terms as Perl itself, either Perl version 5.10.0 or,
1201at your option, any later version of Perl 5 you may have available.
1202
1203
1204=cut
Note: See TracBrowser for help on using the repository browser.