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

Last change on this file since 1909 was 1909, checked in by nanardon, 8 years ago

Avoid some perl warning

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