Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??
hello nuns and monks,

I want to develop for a bunch of friends (no more than 30) a little webservice consumed by a client I provide them and I want to take the occasion to try mojolicious as it was several times suggested to me (thanks ;).

Security will be not a big concern as nothing critical will pass on, but I want to be sure my service will be used only if I can control the genuinity of the client.

Basically I will provide two pieces of text: TEXT_A and TEXT_B.

The client will be a simple perl script using LWP::UserAgent (see below for the code used as test) but I suppose to know how to deal with this (well I have to reuse the cookie set by the server. I only have to tell it to initialize a jar? LWP::UserAgent->new(cookie_jar => {}) no?).

The client-server interaction will be something like this:

  1. the client send a request using basic authentication (user and password sent in the requested URL)
  2. the server check if username and password sent by the client are valid
  3. the server check if the remote IP is allowed and, if so and authentication was done, set up a cookie with a session and send back as only response TEXT_A.
  4. the client receive TEXT_A and show it.
  5. the client, after other short activities, ask to the server for TEXT_B
  6. the server check if there is a valid session and, if so, send TEXT_B
  7. the server close the session.

After some reading and some unwise cut and paste, I imagined something like the following but I'd very like to ear from you where I'm wrong and how things can be done in a better way.

use strict; use warnings; use Mojolicious::Lite; plugin 'ClientIP'; plugin 'basic_auth_plus'; # sample data for authentication my %accepted_IPs = ( '8.8.8.8' => 1 ); my %users = ( usr1 => 'pwd1', usr2 => 'pwd2'); # sample data to send back to client my $text_a = 'Né più mai toccherò le sacre sponde'; my $text_b = 'ove il mio corpo fanciulletto giacque,'; # I expect all non specified routes to answer 404, right? # get text_a. Steps 2) and 3) of the above description get '/get_first' => sub { my $self = shift; my $remote_IP = $self->client_ip; # check username and password my ($href, $auth_ok) = $self->basic_auth( realm => sub { # @_ contains username and password if ( exists $users{$_[0]} and $users{$_[0]} eq $_[1]) +{ # log the correct login $self->log->info( "$_[0] from $remote_IP login OK" + ); return 1; } } ); # reject unwanted remote IP unless ( exists $accepted_IPs{ $remote_IP } ){ # log unwanted IP $self->log->warn( "$href->{username} logged from unwanted IP: +$remote_IP" ); # reject the request return $self->render( status => 401, text => 'unauthorized', ) } # process the request.. if ( $auth_ok ) { # set the session cookie valid for 2 minutes $self->session( expiration => 120, is_my_user_authenticated => + 1 ); # log the action $self->log->info( "sent TEXT_A to $remote_IP" ); # render TEXT_A return $self->render( status => 200, text => $text_a, ); } # .. or reject it else { # log a failed attempt $self->log->warn( "bad login attempt from valid IP $remote_IP + using user $href->{username}" ); # reject return $self->render( status => 401, text => 'unauthorized', ); } }; # get text_b. Steps 6) and 7) of the above description get '/get_second' => sub { my $self = shift; my $remote_IP = $self->client_ip; # check the previously set cookie session if ( $self->session( 'is_my_user_authenticated') == 1 ) { # expire the session cookie $self->session( expires => 1, is_my_user_authenticated => 0 ); # log the action $self->log->info( "sent TEXT_B to $remote_IP" ); # render TEXT_B return $self->render( status => 200, text => $text_b, ); } else { # log the action $self->log->warn( "unexpected request of TEXT_B from $remote_I +P: rejected ". "authenticated: $self->session( 'is_my_use +r_authenticated') ". "expiration: $self->session( 'expiration') +"); # reject return $self->render( status => 401, text => 'unauthorized', ); } }; app->start;

Here the client I used as test ( server: 10.0.0.1 client: 8.8.8.8 ):

use strict; use warnings; use LWP::UserAgent; use HTTP::Request::Common; my $ua = LWP::UserAgent->new(); $ua->cookie_jar( {} ); print "FIRST REQUEST\n\n"; my $request = GET 'http://10.0.0.1/get_first'; $request->authorization_basic('usr1', 'pwd1'); my $response = $ua->request($request); print $response->as_string(); sleep 3; print "SECOND REQUEST\n\n"; my $request2 = GET 'http://10.0.0.1/get_second'; my $response2 = $ua->request($request2); print $response2->as_string(); sleep 3; print "\nATTENTION: next should fail..\n\n"; my $request3 = GET 'http://10.0.0.1/get_second'; my $response3 = $ua->request($request3); print $response3->as_string();

with this output (dont mind the encoding of the ouput.. ;):

FIRST REQUEST HTTP/1.0 200 OK Date: Mon, 15 Jun 2020 11:26:18 GMT Date: Mon, 15 Jun 2020 11:26:18 GMT Server: HTTP::Server::PSGI Content-Length: 38 Content-Type: text/html;charset=UTF-8 Client-Date: Mon, 15 Jun 2020 11:26:20 GMT Client-Peer: 10.0.0.1:80 Client-Response-Num: 1 Set-Cookie: mojolicious=eyJleHBpcmF0aW9uIjoxMjAsImV4cGlyZXMiOjE1OTIyMj +A0OTgsImlzX215X3VzZXJfYXV0aGVudGljYXRlZCI6MX0---d9029065f9456259d6cfd +b50c3adee1d4a2f3b65; expires=Mon, 15 Jun 2020 11:28:18 GMT; path=/; H +ttpOnly; SameSite=Lax N├® pi├╣ mai toccher├▓ le sacre sponde SECOND REQUEST HTTP/1.0 200 OK Date: Mon, 15 Jun 2020 11:26:21 GMT Date: Mon, 15 Jun 2020 11:26:21 GMT Server: HTTP::Server::PSGI Content-Length: 38 Content-Type: text/html;charset=UTF-8 Client-Date: Mon, 15 Jun 2020 11:26:24 GMT Client-Peer: 10.0.0.1:80 Client-Response-Num: 1 Set-Cookie: mojolicious=eyJleHBpcmF0aW9uIjoxMjAsImV4cGlyZXMiOjEsImlzX2 +15X3VzZXJfYXV0aGVudGljYXRlZCI6MH0---2f2a402a06de6b37f1eac53b7cbb79d5e +08f04e7; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/; HttpOnly; Sam +eSite=Lax ove il mio corpo fanciulletto giacque, ATTENTION: next should fail.. HTTP/1.0 401 Unauthorized Date: Mon, 15 Jun 2020 11:26:24 GMT Date: Mon, 15 Jun 2020 11:26:24 GMT Server: HTTP::Server::PSGI Content-Length: 12 Content-Type: text/html;charset=UTF-8 Client-Date: Mon, 15 Jun 2020 11:26:27 GMT Client-Peer: 10.0.0.1:80 Client-Response-Num: 1 Client-Warning: Missing Authenticate header unauthorized

and these log lines (newlines added for readability):

HTTP::Server::PSGI: Accepting connections at http://0:80/ [2020-06-15 13:26:18.41707] [29291] [debug] [MfmL-Zeg] GET "/get_first +" [2020-06-15 13:26:18.41765] [29291] [debug] [MfmL-Zeg] Routing to a ca +llback [2020-06-15 13:26:18.41810] [29291] [info] [MfmL-Zeg] usr1 from 8.8.8. +8 login OK [2020-06-15 13:26:18.41820] [29291] [debug] Your secret passphrase nee +ds to be changed [2020-06-15 13:26:18.41832] [29291] [info] [MfmL-Zeg] sent TEXT_A to 8 +.8.8.8 [2020-06-15 13:26:18.41865] [29291] [debug] [MfmL-Zeg] 200 OK (0.00157 +5s, 634.921/s) 8.8.8.8 - - [15/Jun/2020:13:26:18 +0200] "GET /get_first HTTP/1.1" 200 + - "-" "libwww-perl/6.26" [2020-06-15 13:26:21.60148] [29291] [debug] [cC2TWI3Q] GET "/get_secon +d" [2020-06-15 13:26:21.60208] [29291] [debug] [cC2TWI3Q] Routing to a ca +llback [2020-06-15 13:26:21.60249] [29291] [info] [cC2TWI3Q] sent TEXT_B to 8 +.8.8.8 [2020-06-15 13:26:21.60272] [29291] [debug] [cC2TWI3Q] 200 OK (0.00123 +4s, 810.373/s) 8.8.8.8 - - [15/Jun/2020:13:26:21 +0200] "GET /get_second HTTP/1.1" 20 +0 - "-" "libwww-perl/6.26" [2020-06-15 13:26:24.68206] [29291] [debug] [jscLU00Y] GET "/get_secon +d" [2020-06-15 13:26:24.68244] [29291] [debug] [jscLU00Y] Routing to a ca +llback Use of uninitialized value in numeric eq (==) at /root/srv01/srv01.pl +line 74, <DATA> line 755. [2020-06-15 13:26:24.68275] [29291] [warn] [jscLU00Y] unexpected reque +st of TEXT_B from 8.8.8.8: rejected authenticated: Mojolicious::Contr +oller=HASH(0x316b0a0)->session( 'is_my_user_authenticated') expiratio +n: Mojolicious::Controller=HASH(0x316b0a0)->session( 'expiration') [2020-06-15 13:26:24.68341] [29291] [debug] [jscLU00Y] 401 Unauthorize +d (0.001321s, 757.002/s) 8.8.8.8 - - [15/Jun/2020:13:26:24 +0200] "GET /get_second HTTP/1.1" 40 +1 - "-" "libwww-perl/6.26"

I'd really like to know: is the above sketch correct, in its logic and syntax? The basic authentication and the session management is appropriate (expiration, is_my_user_authenticated and the expires parameters in the cookie)? The logging is done in the right way? I missed something important? How to deploy? I used plackup for testing purpose. I see debug infos in logs: why they are there (happy to see them atm..)? What strings like [MfmL-Zeg] means in loglines?

Thanks for reading

L*

There are no rules, there are no thumbs..
Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

In reply to first steps with Mojolicious::Lite by Discipulus

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-25 19:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found