1package Crypt::Random::Source::Factory;
2# ABSTRACT: Load and instantiate sources of random data
3
4our $VERSION = '0.12';
5
6use Moo;
7use Carp qw(croak);
8use Module::Find;
9use Module::Runtime qw(require_module);
10use Types::Standard qw(ClassName Bool ArrayRef Str);
11use namespace::clean;
12
13sub get {
14    my ( $self, %args ) = @_;
15
16    my $type = delete $args{type} || "any";
17
18    my $method = "new_$type";
19
20    $self->can($method) or croak "Don't know how to create a source of type $type";
21
22    $self->$method(%args);
23}
24
25sub get_weak {
26    my ( $self, @args ) = @_;
27    $self->get( @args, type => "weak" );
28}
29
30sub get_strong {
31    my ( $self, @args ) = @_;
32    $self->get( @args, type => "strong" );
33}
34
35has weak_source => (
36    isa => ClassName,
37    is  => "rw",
38    lazy => 1,
39    builder => 1,
40    clearer    => "clear_weak_source",
41    predicate  => "has_weak_source",
42    handles    => { new_weak => "new" },
43);
44
45sub _build_weak_source {
46    my $self = shift;
47    $self->best_available(@{ $self->weak_sources });
48}
49
50has strong_source => (
51    isa => ClassName,
52    is  => "rw",
53    lazy => 1,
54    builder => 1,
55    clearer    => "clear_strong_source",
56    predicate  => 'has_strong_source',
57    handles    => { new_strong => "new" },
58);
59
60sub _build_strong_source {
61    my $self = shift;
62    $self->best_available(@{ $self->strong_sources });
63}
64
65has any_source => (
66    isa => ClassName,
67    is  => "rw",
68    lazy => 1,
69    builder => 1,
70    clearer    => "clear_any_source",
71    predicate  => 'has_any_source',
72    handles    => { new_any => 'new' },
73);
74
75sub _build_any_source {
76    my $self = shift;
77    $self->weak_source || $self->strong_source;
78}
79
80has scan_inc => (
81    is  => "ro",
82    isa => Bool,
83    lazy => 1,
84    builder => 1,
85    clearer => 'clear_scan_inc',
86    predicate => 'has_scan_inc',
87);
88
89sub _build_scan_inc {
90    my $self = shift;
91
92    if ( exists $ENV{CRYPT_RANDOM_NOT_PLUGGABLE} ) {
93        return !$ENV{CRYPT_RANDOM_NOT_PLUGGABLE};
94    } else {
95        return 1;
96    }
97}
98
99has weak_sources => (
100    isa => ArrayRef[Str],
101    is  => "rw",
102    lazy => 1,
103    builder => 1,
104    clearer => 'clear_weak_sources',
105    predicate => 'has_weak_sources',
106);
107
108sub _build_weak_sources {
109    my $self = shift;
110
111    if ( $self->scan_inc ) {
112        $self->locate_sources("Weak");
113    } else {
114        return [qw(
115            Crypt::Random::Source::Weak::devurandom
116            Crypt::Random::Source::Weak::openssl
117            Crypt::Random::Source::Weak::rand
118        )];
119    }
120}
121
122has strong_sources => (
123    isa => ArrayRef[Str],
124    is  => "rw",
125    lazy => 1,
126    builder => 1,
127    clearer => 'clear_strong_sources',
128    predicate => 'has_strong_sources',
129);
130
131sub _build_strong_sources {
132    my $self = shift;
133
134    if ( $self->scan_inc ) {
135        return $self->locate_sources("Strong");
136    } else {
137        return [qw(
138            Crypt::Random::Source::Strong::devrandom
139            Crypt::Random::Source::Strong::egd
140        )];
141    }
142}
143
144sub best_available {
145    my ( $self, @sources ) = @_;
146
147    my @available = grep { local $@; eval { require_module($_); $_->available }; } @sources;
148
149    my @sorted = sort { $b->rank <=> $a->rank } @available;
150
151    wantarray ? @sorted : $sorted[0];
152}
153
154sub first_available {
155    my ( $self, @sources ) = @_;
156
157    foreach my $class ( @sources ) {
158        local $@;
159        return $class if eval { require_module($class); $class->available };
160    }
161}
162
163sub locate_sources {
164    my ( $self, $category ) = @_;
165    my @sources = findsubmod "Crypt::Random::Source::$category";
166    # Untaint class names (which are tainted in taint mode because
167    # they came from the disk).
168    ($_) = $_ =~ /^(.*)$/ foreach @sources;
169    return \@sources;
170}
171
1721;
173
174=pod
175
176=encoding UTF-8
177
178=head1 NAME
179
180Crypt::Random::Source::Factory - Load and instantiate sources of random data
181
182=head1 VERSION
183
184version 0.12
185
186=head1 SYNOPSIS
187
188    use Crypt::Random::Source::Factory;
189
190    my $f = Crypt::Random::Source::Factory->new;
191
192    my $strong = $f->get_strong;
193
194    my $weak = $f->get_weak;
195
196    my $any = $f->get;
197
198=head1 DESCRIPTION
199
200This class implements a loading and instantiation factory for
201L<Crypt::Random::Source> objects.
202
203If C<$ENV{CRYPT_RANDOM_NOT_PLUGGABLE}> is set then only a preset list of
204sources will be tried. Otherwise L<Module::Find> will be used to locate any
205installed sources, and use the first available one.
206
207=head1 METHODS
208
209=head2 get %args
210
211Instantiate any random source, passing %args to the constructor.
212
213The C<type> argument can be C<weak>, C<strong> or C<any>.
214
215=head2 get_weak %args
216
217=head2 get_strong %args
218
219Instantiate a new weak or strong random source.
220
221=head1 SUPPORT
222
223Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Crypt-Random-Source>
224(or L<bug-Crypt-Random-Source@rt.cpan.org|mailto:bug-Crypt-Random-Source@rt.cpan.org>).
225
226=head1 AUTHOR
227
228יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
229
230=head1 COPYRIGHT AND LICENCE
231
232This software is copyright (c) 2008 by Yuval Kogman.
233
234This is free software; you can redistribute it and/or modify it under
235the same terms as the Perl 5 programming language system itself.
236
237=cut
238
239__END__
240
241
242# ex: set sw=4 et:
243