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

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

Allow to set allowed_values insde database instead config, this work only for SQL base

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