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

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

Add task to warn manager about expired user, then unexporting them

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