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

Last change on this file since 2475 was 2475, checked in by nanardon, 3 years ago

start samba support: manage sambaSID

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