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

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

Never build filter based data with unexported object

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