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

Last change on this file since 2605 was 2605, checked in by nanardon, 2 months ago

Avoid undef

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