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

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

Speed up attribute registration checks

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