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

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