File Coverage

lib/OAuthomatic/Internal/MicroWeb.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package OAuthomatic::Internal::MicroWeb;
2             # ABSTRACT: temporary embedded web server used internally - management code
3              
4              
5 1     1   703 use namespace::sweep;
  0            
  0            
6             use Moose;
7             use MooseX::AttributeShortcuts;
8             use MooseX::Types::Path::Tiny qw/AbsDir AbsPath/;
9             use Path::Tiny qw/path/;
10             use threads;
11             use Thread::Queue;
12             use Const::Fast;
13             use OAuthomatic::Internal::MicroWebSrv;
14             use OAuthomatic::Error;
15              
16             const my $THREAD_START_TIMEOUT => 10;
17             const my $FORM_FILL_WARNING_TIMEOUT => 1 * 60;
18              
19              
20             has 'config' => (
21             is => 'ro', isa => 'OAuthomatic::Config', required => 1,
22             handles=>[ 'app_name', 'html_dir', 'debug' ]);
23              
24              
25             has 'server' => (
26             is => 'ro', isa => 'OAuthomatic::Server', required => 1,
27             handles => [
28             'site_name',
29             'site_client_creation_page',
30             'site_client_creation_desc',
31             'site_client_creation_help',
32             ]);
33              
34              
35             has 'port' => (
36             is=>'lazy', isa=>'Int', required=>1, default => sub {
37             require Net::EmptyPort;
38             return Net::EmptyPort::empty_port();
39             });
40              
41              
42             has 'template_dir' => (
43             is=>'lazy', isa=>AbsDir, required=>1, coerce=>1, default=>sub {
44             return $_[0]->html_dir->child("templates");
45             });
46              
47              
48             has 'static_dir' => (
49             is=>'lazy', isa=>AbsDir, required=>1, coerce=>1, default=>sub {
50             return $_[0]->html_dir->child("static");
51             });
52              
53              
54             has 'verbose' => (is=>'ro', isa=>'Bool');
55              
56              
57             has 'callback_path' => (is=>'ro', isa=>'Str', default=>sub {"/oauth_granted"});
58              
59              
60             has 'client_key_path' => (is=>'ro', isa=>'Str', default=>sub {"/client_key"});
61              
62             has 'root_url' => (is=>'lazy', default=>sub{ "http://localhost:". $_[0]->port });
63             has 'callback_url' => (is=>'lazy', default=>sub{ $_[0]->root_url . $_[0]->callback_path });
64             has 'client_key_url' => (is=>'lazy', default=>sub{ $_[0]->root_url . $_[0]->client_key_path });
65              
66             has '_oauth_queue' => (is=>'ro', builder=>sub{Thread::Queue->new()});
67             has '_client_key_queue' => (is=>'ro', builder=>sub{Thread::Queue->new()});
68              
69             has 'is_running' => (is => 'rwp', isa => 'Bool');
70              
71              
72             sub start {
73             my $self = shift;
74              
75             OAuthomatic::Error::Generic->throw(
76             ident => "Server is already running")
77             if $self->is_running;
78              
79             print "[OAuthomatic] Spawning embedded web server thread\n";
80              
81             $self->{thread} = threads->create(
82             sub {
83             my($app_name,
84             $site_name,
85             $site_client_creation_page,
86             $site_client_creation_desc,
87             $site_client_creation_help,
88             $static_dir,
89             $template_dir,
90             $port,
91             $callback_path,
92             $client_key_path,
93             $debug,
94             $verbose,
95             $oauth_queue,
96             $client_key_queue) = @_;
97              
98             my $srv = OAuthomatic::Internal::MicroWebSrv->new(
99             app_name => $app_name,
100             site_name => $site_name,
101             site_client_creation_page => $site_client_creation_page,
102             site_client_creation_desc => $site_client_creation_desc,
103             site_client_creation_help => $site_client_creation_help,
104             static_dir => $static_dir,
105             template_dir => $template_dir,
106             port => $port,
107             callback_path => $callback_path,
108             client_key_path => $client_key_path,
109             debug => $debug,
110             verbose => $verbose,
111             oauth_queue => $oauth_queue,
112             client_key_queue => $client_key_queue,
113              
114             );
115             $srv->run();
116             },
117             $self->app_name,
118             $self->site_name,
119             $self->site_client_creation_page,
120             $self->site_client_creation_desc,
121             $self->site_client_creation_help,
122             $self->static_dir->stringify,
123             $self->template_dir->stringify,
124             $self->port,
125             $self->callback_path || OAuthomatic::Error::Generic->throw(ident => "No callback_path"),
126             $self->client_key_path || OAuthomatic::Error::Generic->throw(ident => "No client_key_path"),
127             $self->debug,
128             $self->verbose,
129             $self->_oauth_queue,
130             $self->_client_key_queue);
131              
132             # Reading start signal
133             $self->_oauth_queue->dequeue_timed($THREAD_START_TIMEOUT)
134             or OAuthomatic::Error::Generic->throw(
135             ident => "Failed to start embedded web",
136             extra => "Failed to receive completion info in $THREAD_START_TIMEOUT seconds. Is system heavily overloaded?");
137              
138             $self->_set_is_running(1);
139             return 1;
140             }
141              
142              
143             sub stop {
144             my $self = shift;
145              
146             print "[OAuthomatic] Shutting down embedded web server\n" if $self->debug;
147              
148             $self->{thread}->kill('HUP');
149             $self->{thread}->join;
150              
151             $self->_set_is_running(0);
152             return 1;
153             }
154              
155             has '_usage_counter' => (is=>'rw', isa=>'Int', default=>0);
156              
157              
158             sub start_using {
159             my $self = shift;
160             $self->start unless $self->is_running;
161             $self->_usage_counter($self->_usage_counter + 1);
162             return 1;
163             }
164              
165              
166             sub finish_using {
167             my $self = shift;
168             my $counter = $self->_usage_counter - 1;
169             $self->_usage_counter($counter);
170             if($counter <= 0 && $self->is_running) {
171             $self->stop;
172             }
173             return 1;
174             }
175              
176              
177              
178             sub wait_for_oauth_grant {
179             my $self = shift;
180             my $reply;
181             while(1) {
182             $reply = $self->_oauth_queue->dequeue_timed($FORM_FILL_WARNING_TIMEOUT);
183             last if $reply;
184             print "Callback still not received. Please, accept the authorization in the browser (or Ctrl-C me if you changed your mind)\n";
185             }
186              
187             unless($reply->{verifier}) {
188              
189             # FIXME: provide http request
190              
191             if($reply->{oauth_problem}) {
192             OAuthomatic::Error::Generic->throw(
193             ident => "OAuth access rejected",
194             extra => "Attempt to get OAuth authorization was rejected. Error code: $reply->{oauth_problem}",
195             );
196             } else {
197             OAuthomatic::Error::Generic->throw(
198             ident => "Invalid OAuth callback",
199             extra => "Failed to read verifier. Most likely this means some error/omission in OAuthomatic code.\nI am so sorry...\n");
200             }
201             }
202              
203             return unless %$reply;
204             return OAuthomatic::Types::Verifier->new($reply);
205             }
206              
207              
208             sub wait_for_client_cred {
209             my $self = shift;
210             my $reply;
211             while(1) {
212             $reply = $self->_client_key_queue->dequeue_timed($FORM_FILL_WARNING_TIMEOUT);
213             last if $reply;
214             print "Form still not filled. Please, fill the form shown (or Ctrl-C me if you changed your mind)\n";
215             }
216             return unless %$reply;
217             return return OAuthomatic::Types::ClientCred->new(
218             data => $reply,
219             remap => {"client_key" => "key", "client_secret" => "secret"});
220             }
221              
222             1;
223              
224             __END__
225              
226             =pod
227              
228             =encoding UTF-8
229              
230             =head1 NAME
231              
232             OAuthomatic::Internal::MicroWeb - temporary embedded web server used internally - management code
233              
234             =head1 VERSION
235              
236             version 0.02
237              
238             =head1 DESCRIPTION
239              
240             Utility class used internally by OAuthomatic: temporary web server
241             spawned in separate thread, used to receive final redirect of OAuth
242             sequence, and to present additional pages to the user.
243              
244             This module provides methods to manage this server and communicate
245             with it, L<OAuthomatic::Internal::MicroWebSrv> contains it's actual
246             implementation.
247              
248             =head1 PARAMETERS
249              
250             =head2 port
251              
252             Port the helper runs at. By default allocated randomly.
253              
254             =head2 template_dir
255              
256             Directory containing page templates. By default, use templates
257             provided with OAuthomatic (according to C<html_dir> param).
258              
259             =head2 static_dir
260              
261             Directory containing static files referenced by templates. By default,
262             use templates provided with OAuthomatic (according to C<html_dir>
263             param).
264              
265             =head2 verbose
266              
267             Enable console logging of web server interactions.
268              
269             =head2 callback_path
270              
271             URL path used in OAuth callback (/oauth_granted by default).
272              
273             =head2 client_key_path
274              
275             URL path used in user interactions (/client_key by default).
276              
277             =head1 METHODS
278              
279             =head2 start
280              
281             Start embedded web server. To be called (from main thread) before any ineractions begin.
282              
283             =head2 stop
284              
285             Stop embedded web server. To be called (from main thread) after OAuth is properly configured.
286              
287             =head2 start_using
288              
289             Starts if not yet running. Increases usage counter.
290              
291             =head2 finish_using
292              
293             Decreass usage counter. Stops if it tropped to 0.
294              
295             =head2 wait_for_oauth_grant
296              
297             Wait until OAuth post-rights-grant callback arrives and return tokens it provided.
298             Blocks until then. Throws proper error if failed.
299              
300             To be called from the main thread.
301              
302             =head2 wait_for_client_cred
303              
304             Wait until user entered application tokens. Blocks until then.
305              
306             To be called from the main thread.
307              
308             =head1 ATTRIBUTES
309              
310             =head2 config
311              
312             L<OAuthomatic::Config> object used to bundle various configuration params.
313              
314             =head2 server
315              
316             L<OAuthomatic::Server> object used to bundle server-related configuration params.
317              
318             =head1 AUTHOR
319              
320             Marcin Kasperski <Marcin.Kasperski@mekk.waw.pl>
321              
322             =head1 COPYRIGHT AND LICENSE
323              
324             This software is copyright (c) 2015 by Marcin Kasperski.
325              
326             This is free software; you can redistribute it and/or modify it under
327             the same terms as the Perl 5 programming language system itself.
328              
329             =cut