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

Last change on this file since 2181 was 2181, checked in by nanardon, 5 years ago

Ensure we register all attributes

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