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

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

Fix: value limitation not checked properly

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