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

Last change on this file since 867 was 867, checked in by nanardon, 13 years ago
  • add tools to check information coherency
  • Property svn:keywords set to Id Rev
File size: 9.2 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 MIME::Base64;
14
15our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
16
17=head1 NAME
18
19LATMOS::Ad - Perl extension for blah blah blah
20
21=head1 SYNOPSIS
22
23  use LATMOS::Accounts::Bases;
24  my $base = LATMOS::Accounts::Bases->new('unix');
25  ...
26
27=head1 DESCRIPTION
28
29Account base access over standard unix file format.
30
31=head1 FUNCTIONS
32
33=cut
34
35=head2 new(%options)
36
37Create a new LATMOS::Ad object for windows AD $domain.
38
39domain / server: either the Ad domain or directly the server
40
41ldap_args is an optionnal list of arguments to pass to L<Net::LDAP>.
42
43=cut
44
45sub new {
46    my ($class, %options) = @_;
47   
48    my $base = {
49        db_conn => $options{db_conn},
50    };
51
52    bless($base, $class);
53}
54
55sub DESTROY {
56    my ($self) = @_;
57    $self->{_db} && $self->{_db}->rollback;
58}
59
60sub db {
61    my ($self) = @_;
62
63    if ($self->{_db} && $self->{_db}->ping) {
64        return $self->{_db};
65    } else {
66        $self->{_db} = DBI->connect_cached(
67            'dbi:Pg:' . $self->{db_conn},
68            undef, undef,
69            {
70                RaiseError => 0,
71                AutoCommit => 0,
72                PrintWarn => 1,
73                PrintError => 1,
74            }
75        ) or do {
76            $self->log(LA_ERR, "Cannot connect to database: %s", $DBI::errstr);   
77            return;
78        };
79        $self->{_db}->do(q(SET SESSION CHARACTERISTICS AS TRANSACTION
80                    ISOLATION LEVEL SERIALIZABLE));
81        $self->{_db}->do(q{set DATESTYLE to 'DMY'});
82        $self->log(LA_DEBUG, 'New connection to DB');
83        return $self->{_db};
84    }
85}
86
87=head2 load
88
89Read file and load data into memory
90
91=cut
92
93sub load {
94    my ($self) = @_;
95    return $self->db ? 1 : 0;   
96}
97
98sub _commit {
99    my ($self) = @_;
100    if ($ENV{LA_NO_COMMIT}) {
101        $self->log(LA_DEBUG, 'DB::COMMIT (ignore due to LA_NO_COMMIT)');
102        return 1;
103    } else {
104        $self->log(LA_DEBUG, 'DB::COMMIT');
105    }
106    $self->{__cache} = undef;
107    $self->db->commit;
108}
109
110sub _rollback {
111    my ($self) = @_;
112    if ($ENV{LA_NO_COMMIT}) {
113        $self->log(LA_DEBUG, 'DB::ROLLBACK (ignore due to LA_NO_COMMIT)');
114        return 1
115    } else {
116        $self->log(LA_DEBUG, 'DB::ROLLBACK');
117    }
118    $self->{__cache} = undef;
119    $self->db->rollback;
120}
121
122sub list_supported_objects {
123    my ($self, @otype) = @_;
124    $self->SUPER::list_supported_objects(qw(site), @otype);
125}
126
127sub current_rev {
128    my ($self) = @_;
129    my $sth = $self->db->prepare_cached(
130        q{select max(rev) from revisions}
131    );
132    $sth->execute;
133    my $res = $sth->fetchrow_hashref;
134    $sth->finish;
135    return ($res->{max});
136} 
137
138
139# Extra non standard functions
140
141sub get_global_value {
142    my ($self, $varname) = @_;
143
144    my $sth = $self->db->prepare_cached(q{
145        select val from settings where varname = ?
146        });
147    $sth->execute($varname);
148    my $res = $sth->fetchrow_hashref;
149    $sth->finish;
150    $res->{val}
151}
152
153sub set_global_value {
154    my ($self, $varname, $value) = @_;
155    my $sth = $self->db->prepare(q{
156        update settings set val = ? where varname = ?
157        });
158    $sth->execute($value, $varname) == 0 and do {
159        my $sth2 = $self->db->prepare(q{
160            insert into settings (val, varname) values (?,?)
161            });
162        $sth2->execute($value, $varname);
163    };
164}
165
166sub generate_rsa_key {
167    my ($self, $password) = @_;
168
169    my $rsa = new Crypt::RSA ES => 'PKCS1v15';
170    my ($public, $private) = $rsa->keygen (
171        Identity  => 'LATMOS-Accounts',
172        Size      => 768,
173        Password  => $password,
174        Verbosity => 0,
175        KF=>'SSH',
176    ) or die $rsa->errstr(); # TODO avoid die
177    return ($public, $private);
178}
179
180sub private_key {
181    my ($self, $password) = @_;
182    my $base = $self;
183    my $serialize = $base->get_global_value('rsa_private_key') or return;
184    my $privkey = Crypt::RSA::Key::Private::SSH->new;
185    $privkey->deserialize(String => [ decode_base64($serialize) ],
186        Passphrase => $password);
187    $privkey
188}
189
190sub get_rsa_password {
191    my ($self) = @_;
192    my $base = $self;
193    my $sth = $base->db->prepare(q{
194        select "name", value from "user" join user_attributes_base
195        on "user".ikey = user_attributes_base.okey
196        where user_attributes_base.attr = 'encryptedPassword'
197    });
198    $sth->execute;
199    my %users;
200    while (my $res = $sth->fetchrow_hashref) {
201        $users{$res->{name}} = $res->{value};
202    }
203    %users
204}
205
206sub store_rsa_key {
207    my ($self, $public, $private) = @_;
208    my $base = $self;
209    $base->set_global_value('rsa_private_key',
210        encode_base64($private->serialize));
211    $base->set_global_value('rsa_public_key',
212        $public->serialize);
213    return;
214}
215
216
217sub find_next_expire_users {
218    my ($self, $expire) = @_;
219
220    my $sth= $self->db->prepare(q{
221        select name from "user" where
222            expire < now() + ?::interval
223            and expire > now()
224            and expire is not null
225            } . ($self->{wexported} ? '' : 'and exported = true') . q{
226            order by expire
227        }
228    );
229    $sth->execute($expire || '1 month');
230    my @users;
231    while (my $res = $sth->fetchrow_hashref) {
232        push(@users, $res->{name});
233    }
234    @users
235}
236
237sub find_expired_users {
238    my ($self, $expire) = @_;
239
240    my $sth= $self->db->prepare(q{
241        select name from "user" where
242            expire < now() - ?::interval
243            and expire is not null
244        } . ($self->{wexported} ? '' : 'and exported = true') . q{
245            order by expire
246        }
247    );
248    $sth->execute($expire || '1 second');
249    my @users;
250    while (my $res = $sth->fetchrow_hashref) {
251        push(@users, $res->{name});
252    }
253    @users
254}
255
256sub rename_nethost {
257    my ($self, $nethostname, $to, %options) = @_;
258    {
259        my $obj = $self->get_object('nethost', $nethostname);
260        my @cname = grep { $_ && $_ ne $to}
261        $obj->get_attributes('cname');
262        $obj->set_c_fields(cname => [ @cname ]) or return;
263    }
264    $self->rename_object('nethost', $nethostname, $to) or return;
265    if ($options{'addcname'}) {
266        my $obj = $self->get_object('nethost', $to);
267        my @cname = grep { $_ } $obj->get_attributes('cname');
268        $obj->set_c_fields(cname => [ @cname, $nethostname ]);
269    }
270    return 1;
271}
272
273sub nethost_exchange_ip {
274    my ($self, $ip1, $ip2) = @_;
275    my ($obj1, $obj2);
276    if (my ($host1) = $self->search_objects('nethost', "ip=$ip1")) {
277        $obj1 = $self->get_object('nethost', $host1);
278    } else {
279        $self->la_log(LA_ERR, "Cannot find host having $ip1");
280        return;
281    }
282    if (my ($host2) = $self->search_objects('nethost', "ip=$ip2")) {
283        $obj2 = $self->get_object('nethost', $host2);
284    } else {
285        $self->la_log(LA_ERR, "Cannot find host having $ip2");
286        return;
287    }
288    if ($obj1->id eq $obj2->id) {
289        $self->la_log(LA_ERR, "Both ip belong to same host (%s)", $obj1->id);
290        return;
291    }
292
293    my @ip1 = grep { $_ && $_ ne $ip1 } $obj1->get_attributes('ip');
294    $obj1->set_c_fields(ip => [ @ip1 ]);
295    my @ip2 = grep { $_ && $_ ne $ip2 } $obj2->get_attributes('ip');
296    $obj2->set_c_fields(ip => [ @ip2, $ip1 ]) or return;
297    $obj1->set_c_fields(ip => [ @ip1, $ip2 ]) or return;
298    return 1;
299}
300
301sub register_attribute {
302    my ($self, $otype, $attribute, $comment) = @_;
303    my $pclass = $self->_load_obj_class($otype) or return;
304    $pclass->register_attribute($self, $attribute, $comment);
305}
306
307sub get_attribute_comment {
308    my ($self, $otype, $attribute) = @_;
309    my $pclass = $self->_load_obj_class($otype) or return;
310    $pclass->get_attribute_comment($self, $attribute);
311}
312
313sub set_attribute_comment {
314    my ($self, $otype, $attribute, $comment) = @_;
315    my $pclass = $self->_load_obj_class($otype) or return;
316    $pclass->set_attribute_comment($self, $attribute, $comment);
317}
318
319sub check_user_manager {
320    $_[0]->_handle_by_unexported('user', 'manager', 'active');
321}
322
323sub check_group_manager {
324    $_[0]->_handle_by_unexported('group', 'managedBy');
325}
326
327sub check_nethost_owner {
328    $_[0]->_handle_by_unexported('nethost', 'owner', 'active');
329}
330
331sub _handle_by_unexported {
332    my ($self, $otype, $refattr, $chkattr) = @_;
333
334    my $ptrotype = $self->attribute($otype, $refattr)->reference();
335
336    my %unhandle;
337    foreach my $objname ($self->search_objects($otype, 'active=1', 'exported=1', "$refattr=*")) {
338        my $obj = $self->get_object($otype, $objname) or next;
339        my $val = $obj->get_attributes($refattr) or next;
340        if (my $refobj = $self->get_object($ptrotype, $val)) {
341            if (!$refobj->get_attributes($chkattr || 'exported')) {
342                $unhandle{$objname} = $val;
343            }
344        } else {
345            $unhandle{$objname} = $val;
346        }
347    }
348    %unhandle;
349}
350
3511;
352
353__END__
354
355=head1 SEE ALSO
356
357=head1 AUTHOR
358
359Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
360
361=head1 COPYRIGHT AND LICENSE
362
363Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
364
365This library is free software; you can redistribute it and/or modify
366it under the same terms as Perl itself, either Perl version 5.10.0 or,
367at your option, any later version of Perl 5 you may have available.
368
369
370=cut
Note: See TracBrowser for help on using the repository browser.