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

Last change on this file since 2035 was 2035, checked in by nanardon, 7 years ago

Add Crypt::Blowfish dependencies

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