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

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

Use std isolation level

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