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

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

Add some test, then fix some bugs

  • Property svn:keywords set to Id Rev
File size: 25.0 KB
Line 
1package LATMOS::Accounts::Bases::Sql;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use base qw(LATMOS::Accounts::Bases);
8use LATMOS::Accounts::Log;
9use 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        my $oalias = $self->GetAlias($otype, $name);
452        $oalias->_update_aliases_ptr();
453        return 1;
454    } else {
455        $self->log(LA_ERR, "Error when creating alias $otype $name");
456        return;
457    }
458}
459
460=head2 RemoveAlias($otype, $name, $for)
461
462Create an object alias named C<$name> for ovbject C<$for>
463
464=cut
465
466sub RemoveAlias {
467    my ($self, $otype, $name) = @_;
468
469    my $pclass = $self->_load_obj_class($otype) or return;
470
471    my $obj = $self->GetAlias($otype, $name) or do {
472        $self->log('Cannot get alias %s/%s for removal', $otype, $name);
473        return;
474    };
475
476    if ($obj->_get_attributes('internobject')) {
477        # Cannot happend: internal are not fetchable
478        $self->log(LA_ERR,'Cannot delete %s/%s: is an internal object', $pclass->type, $name);
479        return;
480    }
481    if ($obj->_get_attributes('nodelete')) {
482        $self->log(LA_ERR,'Cannot delete %s/%s: is write protected', $pclass->type, $name);
483        return;
484    }
485
486    my $id = $obj->Iid;
487
488    my $sth = $self->db->prepare_cached(sprintf(
489        'DELETE FROM %s WHERE %s = ?',
490        $self->db->quote_identifier($pclass->_object_table),
491        $self->db->quote_identifier($pclass->_key_field),
492    ));
493
494    $obj->_update_aliases_ptr;
495    my $res = $sth->execute($name);
496
497    if ($res) {
498        $self->ReportChange(
499            $otype,
500            $name,
501            $id,
502            'Delete', "Alias %s %s deleted", $otype, $name
503        );
504        $self->log(LA_DEBUG, "Alias $otype $name removed");
505        return 1;
506    } else {
507        $self->log(LA_ERR, "Error when removing alias $otype $name");
508        return;
509    }
510}
511
512=head2 get_global_value ($varname)
513
514Return global value set into base
515
516=cut
517
518sub get_global_value {
519    my ($self, $varname) = @_;
520
521    my $sth = $self->db->prepare_cached(q{
522        select val from settings where varname = ?
523        });
524    $sth->execute($varname);
525    my $res = $sth->fetchrow_hashref;
526    $sth->finish;
527    $res->{val}
528}
529
530=head2 set_global_value ($varname, $value)
531
532Set global value.
533
534=cut
535
536sub set_global_value {
537    my ($self, $varname, $value) = @_;
538    my $sth = $self->db->prepare(q{
539        update settings set val = ? where varname = ?
540        });
541    $sth->execute($value, $varname) == 0 and do {
542        my $sth2 = $self->db->prepare(q{
543            insert into settings (val, varname) values (?,?)
544            });
545        $sth2->execute($value, $varname);
546    };
547}
548
549=head2 del_global_value ($varname)
550
551Delete global value from base
552
553=cut
554
555sub del_global_value {
556    my ($self, $varname) = @_;
557
558    my $sth = $self->db->prepare_cached(q{
559        delete from settings where varname = ?
560        });
561    return $sth->execute($varname);
562}
563
564=head2 generate_rsa_key ($password)
565
566Return public and private peer rsa keys
567
568=cut
569
570sub generate_rsa_key {
571    my ($self, $password) = @_;
572
573    my $rsa = new Crypt::RSA ES => 'PKCS1v15';
574    my ($public, $private) = $rsa->keygen (
575        Identity  => 'LATMOS-Accounts',
576        Size      => 2048,
577        Password  => $password,
578        Verbosity => 0,
579    ) or die $rsa->errstr(); # TODO avoid die
580    return ($public, $private);
581}
582
583=head2 private_key ($password)
584
585Load and return private rsa key
586
587=cut
588
589sub private_key {
590    my ($self, $password) = @_;
591    my $base = $self;
592    my $serialize = $base->get_global_value('rsa_private_key') or return;
593    my $string = decode_base64($serialize);
594    my $privkey = $string =~ /^SSH PRIVATE KEY FILE/
595        ? Crypt::RSA::Key::Private::SSH->new
596        : Crypt::RSA::Key::Private->new;
597    $privkey = $privkey->deserialize(
598        String => [ $string ],
599        Password => $password
600    );
601    $privkey->reveal( Password => $password );
602    $privkey;
603}
604
605=head2 get_rsa_password
606
607Return hash with peer username => encryptedPassword
608
609=cut
610
611sub get_rsa_password {
612    my ($self) = @_;
613    my $base = $self;
614    my $sth = $base->db->prepare(q{
615        select "name", value from "user" join user_attributes_base
616        on "user".ikey = user_attributes_base.okey
617        where user_attributes_base.attr = 'encryptedPassword'
618    });
619    $sth->execute;
620    my %users;
621    while (my $res = $sth->fetchrow_hashref) {
622        $users{$res->{name}} = $res->{value};
623    }
624    %users
625}
626
627=head2 store_rsa_key ($public, $private)
628
629Store public and private RSA key info data base
630
631=cut
632
633sub store_rsa_key {
634    my ($self, $public, $private) = @_;
635    my $base = $self;
636    $private->hide;
637    $base->set_global_value('rsa_private_key',
638        encode_base64($private->serialize));
639    $base->set_global_value('rsa_public_key',
640        $public->serialize);
641    return;
642}
643
644=head2 find_next_expire_users ($expire)
645
646Search user expiring in C<$expire> delay
647
648=cut
649
650sub find_next_expire_users {
651    my ($self, $expire) = @_;
652
653    my $sth= $self->db->prepare(q{
654        select name from "user" where
655            expire < now() + ?::interval
656            and expire > now()
657            and expire is not null
658            and internobject = false
659            } . ($self->{wexported} ? '' : 'and exported = true') . q{
660            order by expire
661        }
662    );
663    $sth->execute($expire || '1 month');
664    my @users;
665    while (my $res = $sth->fetchrow_hashref) {
666        push(@users, $res->{name});
667    }
668    @users
669}
670
671=head2 find_expired_users ($expire)
672
673Return list of user going to expires in C<$expire> delay
674
675=cut
676
677sub find_expired_users {
678    my ($self, $expire) = @_;
679
680    my $sth= $self->db->prepare(q{
681        select name from "user" where
682            expire < now() - ?::interval
683            and expire is not null
684            and internobject = false
685        } . ($self->{wexported} ? '' : 'and exported = true') . q{
686            order by expire
687        }
688    );
689    $sth->execute($expire || '1 second');
690    my @users;
691    while (my $res = $sth->fetchrow_hashref) {
692        push(@users, $res->{name});
693    }
694    @users
695}
696
697=head2 rename_nethost ($nethostname, $to, %config)
698
699Facility function to rename computer to new name
700
701=cut
702
703sub rename_nethost {
704    my ($self, $nethostname, $to, %config) = @_;
705    {
706        my $obj = $self->get_object('nethost', $nethostname) or do {
707            $self->log(LA_ERR, 'Unable to rename non exisant host %s', $nethostname);
708            return;
709        };
710        $obj->_delAttributeValue(cname => $to);
711    }
712    $self->rename_object('nethost', $nethostname, $to) or return;
713    if ($config{'addcname'}) {
714        my $obj = $self->get_object('nethost', $to);
715        $obj->_addAttributeValue(cname => $nethostname);
716    }
717    return 1;
718}
719
720=head2 nethost_exchange_ip ($ip1, $ip2)
721
722Exchange ip1 with ip2 in base
723
724=cut
725
726sub nethost_exchange_ip {
727    my ($self, $ip1, $ip2) = @_;
728    my ($obj1, $obj2);
729    if (my ($host1) = $self->search_objects('nethost', "ip=$ip1")) {
730        $obj1 = $self->get_object('nethost', $host1);
731    } else {
732        $self->log(LA_ERR, "Cannot find host having $ip1");
733        return;
734    }
735    if (my ($host2) = $self->search_objects('nethost', "ip=$ip2")) {
736        $obj2 = $self->get_object('nethost', $host2);
737    } else {
738        $self->log(LA_ERR, "Cannot find host having $ip2");
739        return;
740    }
741    if ($obj1->id eq $obj2->id) {
742        $self->log(LA_ERR, "Both ip belong to same host (%s)", $obj1->id);
743        return;
744    }
745
746    $self->log(LA_NOTICE, "Exchanging IP between %s and %s", $obj1->id, $obj2->id);
747    $obj1->delAttributeValue('ip', $ip1) or return;
748    $obj2->delAttributeValue('ip', $ip2) or return;
749    $obj1->addAttributeValue('ip', $ip2) or return;
750    $obj2->addAttributeValue('ip', $ip1) or return;
751    return 1;
752}
753
754=head1 ATTRIBUTES FUNCTIONS
755
756=head2 register_attribute ($otype, $attribute, $comment)
757
758Register a new attribute in base
759
760=cut
761
762sub register_attribute {
763    my ($self, $otype, $attribute, $comment) = @_;
764    my $pclass = $self->_load_obj_class($otype) or return;
765    $pclass->register_attribute($self, $attribute, $comment);
766}
767
768=head2 is_registered_attribute ($otype, $attribute)
769
770Return true is attribute already exists
771
772=cut
773
774sub is_registered_attribute {
775    my ($self, $otype, $attribute) = @_;
776    my $pclass = $self->_load_obj_class($otype) or return;
777    $pclass->is_registered_attribute($self, $attribute);
778}
779
780=head2 get_attribute_comment ($otype, $attribute)
781
782Return the comment associated to attribute
783
784=cut
785
786sub get_attribute_comment {
787    my ($self, $otype, $attribute) = @_;
788    my $pclass = $self->_load_obj_class($otype) or return;
789    $pclass->get_attribute_comment($self, $attribute);
790}
791
792=head2 set_attribute_comment ($otype, $attribute, $comment)
793
794Set comment to attribute
795
796=cut
797
798sub set_attribute_comment {
799    my ($self, $otype, $attribute, $comment) = @_;
800    my $pclass = $self->_load_obj_class($otype) or return;
801    $pclass->set_attribute_comment($self, $attribute, $comment);
802}
803
804=head2 get_datarequest ($id)
805
806Return user request C<$id>
807
808=cut
809
810sub get_datarequest {
811    my ($self, $id) = @_;
812
813    my $sth = $self->db->prepare(q{
814        select name from request
815        where id = ?
816        });
817    $sth->execute($id);
818    if (my $res = $sth->fetchrow_hashref) {
819        my $accreq = $self->get_object('accreq', $res->{name});
820        return LATMOS::Accounts::Bases::Sql::DataRequest->new($accreq, $id);
821    } else {
822        return;
823    }
824}
825
826=head2 list_requests
827
828list user request currently waiting in base
829
830=cut
831
832sub list_requests {
833    my ($self, $due) = @_;
834
835    my $sth = $self->db->prepare(
836        sprintf(
837            q{
838            select id from request
839            where done is null
840            %s
841            order by apply
842            },
843            defined($due)
844                ? 'and apply ' . ($due ? '<' : '>=') . ' now()'
845                : ''
846        )
847    );
848    $sth->execute;
849    my @ids;
850    while (my $res = $sth->fetchrow_hashref) {
851        push(@ids, $res->{id});
852    }
853
854    @ids
855}
856
857=head2 list_requests_by_submitter ($id)
858
859list user request currently waiting in base ask by user C<$id>
860
861=cut
862
863sub list_requests_by_submitter {
864    my ($self, $id) = @_;
865
866    my $sth = $self->db->prepare(q{
867        select id from request
868        where done is null and "user" = ?
869        order by apply
870    });
871    $sth->execute($id);
872    my @ids;
873    while (my $res = $sth->fetchrow_hashref) {
874        push(@ids, $res->{id});
875    }
876
877    @ids
878}
879
880
881=head2 list_request_by_object ($otype, $id, $req)
882
883Return the list of pending request for a specific object
884
885C<$req> is an optional forms name to limit search
886
887=cut
888
889sub list_request_by_object {
890    my ($self, $otype, $id, $req) = @_;
891
892    my $sth = $self->db->prepare(q{
893        select * from request join
894        accreq on request.name = accreq.name
895        join accreq_attributes on accreq_attributes.okey = accreq.ikey
896        where
897        request.applied is NULL and
898        accreq_attributes.attr = 'oType' and
899        accreq_attributes.value = ?
900        and request.object = ?
901    } .
902    ($req ? ' and request.name = ? ' : '')
903    . q{
904        order by apply
905    });
906    $sth->execute($otype, $id, ($req ? ($req) : ()));
907    my @ids;
908    while (my $res = $sth->fetchrow_hashref) {
909        push(@ids, $res->{id});
910    }
911
912    @ids
913}
914
915=head2 list_pending_requests
916
917List user request to apply
918
919=cut
920
921sub list_pending_requests {
922    my ($self) = @_;
923
924    my $sth = $self->db->prepare(q{
925        select id from request
926        where done is null
927            and apply < now()
928        order by apply
929    });
930    $sth->execute;
931    my @ids;
932    while (my $res = $sth->fetchrow_hashref) {
933        push(@ids, $res->{id});
934    }
935
936    @ids
937}
938
939=head2 list_auto_pending_requests
940
941List automatic request
942
943=cut
944
945sub list_auto_pending_requests {
946    my ($self) = @_;
947
948    my $sth = $self->db->prepare(q{
949        select id from request
950        where done is null
951            and apply < now()
952            and automated = true
953        order by apply
954    });
955    $sth->execute;
956    my @ids;
957    while (my $res = $sth->fetchrow_hashref) {
958        push(@ids, $res->{id});
959    }
960
961    @ids
962}
963
964sub ReportChange {
965    my ($self, $otype, $name, $ref, $changetype, $message, @args) = @_;
966
967    my $sthmodifiedby = $self->db->prepare(q{
968        UPDATE objects set modifiedby = ? where ikey = ?
969    });
970
971    $sthmodifiedby->execute(
972        $self->user || '@Console',
973        $ref,
974    );
975
976    my $sth = $self->db->prepare(q{
977        INSERT into objectslogs (ikey, irev, otype, name, changetype, username, message)
978        VALUES (?,?,?,?,?,?,?)
979        });
980
981    $sth->execute(
982        $ref,
983        $self->current_rev,
984        $otype,
985        $name,
986        $changetype,
987        $self->user || '@Console',
988        sprintf($message, @args),
989    );
990}
991
992=head2 getobjectlogs($otype, $name)
993
994Return logs for object type C<$otype> having C<$name>.
995
996=cut 
997
998sub getobjectlogs {
999    my ($self, $otype, $name) = @_;
1000
1001    my $sth = $self->db->prepare(q{
1002        select ikey from objectslogs where
1003            otype = ? and
1004            name  = ?
1005        group by ikey
1006    });
1007    $sth->execute($otype, $name);
1008    my @ids;
1009    while (my $res = $sth->fetchrow_hashref) {
1010        push(@ids, $res->{ikey});
1011    }
1012    @ids or return;
1013
1014    my $sth2 = $self->db->prepare(sprintf(
1015        q{
1016            select * from objectslogs where ikey IN (%s)
1017            order by logdate asc
1018        },
1019        join(',', ('?') x scalar(@ids))
1020    ));
1021
1022    $sth2->execute(@ids);
1023    my @logs;
1024    while (my $res = $sth2->fetchrow_hashref) {
1025        push(@logs, $res);
1026    }
1027
1028    return @logs;
1029}
1030
1031=head2 getlogs
1032
1033Return logs for last year
1034
1035=cut
1036
1037sub getlogs {
1038    my ($self) = @_;
1039    my $sth2 = $self->db->prepare(
1040        q{
1041            select * from objectslogs
1042            where logdate > now() - '1 year'::interval
1043            order by logdate asc
1044        },
1045    );
1046
1047    $sth2->execute();
1048    my @logs;
1049    while (my $res = $sth2->fetchrow_hashref) {
1050        push(@logs, $res);
1051    }
1052
1053    return @logs;
1054}
1055
1056
10571;
1058
1059__END__
1060
1061=head1 SEE ALSO
1062
1063=head1 AUTHOR
1064
1065Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
1066
1067=head1 COPYRIGHT AND LICENSE
1068
1069Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
1070
1071This library is free software; you can redistribute it and/or modify
1072it under the same terms as Perl itself, either Perl version 5.10.0 or,
1073at your option, any later version of Perl 5 you may have available.
1074
1075
1076=cut
Note: See TracBrowser for help on using the repository browser.