Adding a custom login page to awstats - perl

I am trying to setup a login page to be used with awstats, so that the content is only viewable by authenticated users.
Ideally, I would like to create my own login page, and if a user is not logged in when the visit the stats page, they are redirected to the login page. (Right now there is no authentication)
The problem is that I don't know how to implement this. I have tried googling this, but the only solutions I could find were to use .htaccess (which I would rather not use in this case if I don't have to)
Has anyone implemented something similar to this?

.htaccess is the right tool for this job, but if you insist, the ancient ancient ancient way
#!/usr/bin/perl --
use strict;
use warnings;
use CGI;
Main( #ARGV );
exit( 0 );
sub Main {
my ( $q ) = CGI->new;
if( $q->param('password') eq 'secret' ){
print ShowAWSTATS($q);
} else {
print ShowLoginForm($q);
}
}
where ShowLoginForm() prints a content header $q->header along with the html for a login form, and ShowAWSTATS prints a content header, and say, some html as provided by awstats.pl
Like Len Jaffe says, there is much much much more that needs to be done, so you want to use .htaccess (its either 3min with .htaccess or hours with anything else)

Related

perl WWW::Mechanize can't seem to find the right form or assign fields or click submit

So I'm trying to create a perl script that logs in to SAP BusinessObjects Central Management Console (CMC) page but it doesn't even look like it's finding the right form or finding the right field or even clicking Submit.
Here's my code:
use strict;
use warnings;
use WWW::Mechanize;
use HTTP::Cookies;
my $mech = WWW::Mechanize->new();
$mech->cookie_jar(HTTP::Cookies->new());
$mech->get("http://myserver:8080/BOE/CMC");
$mech->form_name("_id2");
$mech->field("_id2:logon:CMS", "MYSERVER:6400");
$mech->field("_id2:logon:SAP_SYSTEM", "");
$mech->field("_id2:logon:SAP_CLIENT", "");
$mech->field("_id2:logon:USERNAME", "MYUSER");
$mech->field("_id2:logon:PASSWORD", "MYPWD");
$mech->field("_id2:logon:AUTH_TYPE", "secEnterprise");
$mech->click;
print $mech->content();
When I run it, I don't get any errors but the output I get is the login page again. Even more puzzling, it doesn't seem to be accepting the field values I send it (the output would display default values instead of the values I assign it). Putting in a wrong user or password doesn't change anything - no error but I just get the login page back with default values
I think the script itself is fine since I changed the necessary fields and I was able to log in to our Nagios page (the output page definitely shows Nagios details). I think the CMC page is not so simple, but I need help in figuring out how to make it work.
What I've tried:
1
use Data::Dumper;
print $mech->forms;
print Dumper($mech->forms());
What that gave me is:
Current form is: WWW::Mechanize=HASH(0x243d828)
Part of the Dumper output is:
'attr' => {
'target' => 'servletBridgeIframe',
'style' => 'display:none;',
'method' => 'post'
},
'inputs' => []
I'm showing just that part of the Dumper output because it seems that's the relevant part. When I tried the same thing with our Nagios page, the 'attr' section had a 'name' field which the above doesn't. The Nagios page also had entries for 'inputs' such as 'useralias' and 'password' but the above doesn't have any entries.
2
$mech->form_number(1);
Since I wasn't sure I was referencing the form correctly, I just had it try using the first form it finds (the page only has one form anyway). My result was the same - no error and the output is the login page with default values.
3
I messed around with escaping (with '\') the underscore (_) and colon (:) in the field names.
I've searched and didn't find anything that said I had to escape any characters but it was worth a shot. All I know is, the Nagios page field names only contained letters and it worked.
I got field names from Chrome's developer tool. For example, the User Name form field showed:
<input type="text" id="_id2:logon:USERNAME" name="_id2:logon:USERNAME" value="Administrator">
I don't know if Mechanize has a problem with names starting with underscore or names containing colons.
4
$mech->click("_id2:logon:logonButton");
Since I wasn't sure the "Log On" button was being clicked I tried to specify it but it gave me an error:
No clickable input with name _id2:logon:logonButton at /usr/share/perl5/WWW/Mechanize.pm line 1676
That's probably because there is no name defined on the button (I used the id instead) but I thought it was worth a shot. Here's the code of the button:
<input type="submit" id="_id2:logon:logonButton" value="Log On" class="logonButtonNoHover logon_button_no_hover" onmouseover="this.className = 'logonButtonHover logon_button_hover';" onmouseout="this.className = 'logonButtonNoHover logon_button_no_hover';">
There's only one button on the form anyway so I shouldn't have needed to specify it (I didn't need to for the Nagios page)
5
The interactive shell of Mechanize
Here's the output when I tried to retrieve all forms on the page:
$ perl -MWWW::Mechanize::Shell -eshell
(no url)>get http://myserver:8080/BOE/CMC
Retrieving http://myserver:8080/BOE/CMC(200)
http://myserver:8080/BOE/CMC>forms
Form [1]
POST http://myserver:8080/BOE/CMC/1412201223/admin/logon.faces
Help!
I don't really know perl so I don't know how to troubleshoot this further - especially since I'm not seeing errors. If someone can direct me to other things to try, it would be helpful.
In this age of DOM and Javascript, there's lots of things that can go wrong with Web automation. From your results, it looks like maybe the form is built in browser space, which can be really hard to deal with programmatically.
The way to be sure is to dump the original response and look at the form code it contains.
If that turns out to be your problem, your simplest recourse is something like Mozilla::Mechanize.
When dealing with forms, it can sometimes be easier to replicate the request the form generates than to try to work with the form through Mechanize.
Try using your browser's developer tools to monitor what happens when you log into the site manually (in Firefox or Chrome it'll be under the Network tab), and then generate the same request with Mechanize.
For example, the resulting code MIGHT look something like:
my $post_data => {
'_id2:logon:CMS' => "MYSERVER:6400",
'_id2:logon:SAP_SYSTEM' => "",
'_id2:logon:SAP_CLIENT' => "",
'_id2:logon:USERNAME' => "MYUSER",
'_id2:logon:PASSWORD' => "MYPWD",
'_id2:logon:AUTH_TYPE' => "secEnterprise",
};
$mech->post($url, $post_data);
unless ($mech->success()){
warn "Failed to post to $url: " . $mech->response()->status_line() . "\n";
}
print $mech->content();
Where %post_data should match exactly the data that's passed in the manual post to the site and not just what's in the HTML--the keys or data could be transformed by javascript before the actual post is made.
I had someone more knowledgeable than me give me help. The main hurdle was how the page was constructed in frames and how it operated. Here are the details:
The URL of the frame that contained the login page is "http://myserver:8080/BOE/CMC/0000000000/myuser/logon.faces". The main frame of the page had a form in it, but it wasn't the logon form, which explains why the form from my original code didn't have the logon fields I was expecting.
The other "gotcha" that I ran into was that after a successful logon, the site redirects you to a different URL: "http://myserver:8080/BOE/CMC/0000000000/myuser/App/home.faces?service=%2Fmyuser%2FApp%2F". So to check a successful login, I had to get this URL and check for whatever text I decided to look for.
I also had to refer to the logon form by id and not by name (since the form did not have a name).
Here's the working code:
use strict;
use warnings;
use WWW::Mechanize;
use HTTP::Cookies;
my $mech = WWW::Mechanize->new();
$mech->cookie_jar(HTTP::Cookies->new());
$mech->get("http://myserver:8080/BOE/CMC/0000000000/myuser/logon.faces");
$mech->form_id("_id2");
$mech->field("_id2:logon:CMS", "MYSERVER:6400");
$mech->field("_id2:logon:SAP_SYSTEM", "");
$mech->field("_id2:logon:SAP_CLIENT", "");
$mech->field("_id2:logon:USERNAME", "MyUser");
$mech->field("_id2:logon:PASSWORD", "MyPwd");
$mech->field("_id2:logon:AUTH_TYPE", "secEnterprise");
$mech->click;
$mech->get("http://myserver:8080/BOE/CMC/0000000000/myuser/App/home.faces?service=%2Fmyuser%2FApp%2FappService.jsp&appKind=CMC");
$output_page = $mech->content();
if (index($output_page, "Welcome:") != -1)
{
print "\n\n+++++ Successful login! ++++++\n\n";
}
else
{
print "\n\n----- Login failed!-----\n\n";
}
For validating that I had successfully logged in, I kept it very simple and just searched for the "Welcome:" text (as in "Welcome: MyUser").

Avoid Form from being submitted twice by user with Perl (CGI)

I’m writing a small Perl page that receives a POST method submit. I want to be able to prevent from a single person/computer to submit the form multiple times (to avoid flooding with repetitive submits). But I can’t find any examples or explanations on how to do this in Perl CGI. Could you advise or direct me to some examples?
I understand I can use some data from the HTTP header (token?) and/or plant a cookie after the first submit, but I’m not sure how.
Any help will be appreciated.
Best Regards,
-Arseny
The most simple way of avoiding users clicking the button several times would be to add some Javascript to your page. That would ofc not work for scripts or for i.e. pressing F5.
<input type="submit" name="go" id="go" value="go" onclick="this.disabled='disabled'"/>
You could also write a log file/database on the server that holds the IP address of the user and the timestamp, and check whether he as already submitted. Doing that in addition to setting and checking a cookie is probably the way to go.
For cookies, see cookies in the CGI doc. Simple example:
use strict; use warnings;
use CGI;
my $q = new CGI;
my $submitted = 0;
if ($q->cookie('submitted ')) {
$submitted = 1;
}
# Here you could place the file/db check to also set $voted
if ($submitted) {
print $q->header('text/plain');
print "You have already submitted!";
} else {
# Do stuff with the form, like $q->param('foo')...
# Once you're done, place the cookie
print $q->header(
-type => 'text/plain',
-cookie => $q->cookie(
-name => 'submitted',
-value => 1,
-expires => '+1y',
));
}

Forms and POST using perl

I have this which is supposed to fill the email form on the http://faceoook.com/recover.php
and as you know, you can search by email,name or phone number.
So I am trying to search by email, and get the content of that page after the search has been completed to see whether the profile is found or not, but the code doesn't seem to work.
use HTTP::Request::Common;
use LWP::UserAgent;
$email="blabla\#hotmail.com";
my %data=(email=>$email);
my $user_agent = 'Mozilla/6.0';
my $Browser = LWP::UserAgent->new;
$Browser->agent($user_agent);
$ua=$Browser->post('https://www.facebook.com/recover.php',\%data);
if($ua->content=~/couldn\'t/){ #"couldn't" is part of the message displayed when
print "Not Found"; # input doesn't match
}
elsif ($ua->content=~/name/) {
print "Found";
}
else {
print "Not found";
}
$result=$ua->content;
open FILE,">","me.txt" or die $!;
print FILE $result;
close FILE;
use strict
make it compile under strict
review the manpage for LWP::UserAgent, there's a problem with your code that you'll have to discover on your own so you'll remember
review your variable names in light of the conventions used in the manpage
review the approach (considering the Facebook has an API, IIRC)
no need to escape the single quote in the regex
You should post your request to the URL in the action field of the form (while you're using the URL of the page which shows the form).
Also add any hidden field to your %data.
Have a look at the HTML code of the page (or use some sort of form inspector) to get the correct URL and the hidden fields (javascript code, if present, can further complicate things).
Then use strict (and use warnings as well) as already said by Lumi.

Downloading or requesting a page using a proxy list?

I was wondering if that was possible to request an internet page from its server via a proxy taken from a proxy list.
I don't really know all the exact terms, so I'll just explain what I want: say there is a feature in a website which counts IPs or something alike (perhaps cookies), such as visitors counter. I'd like to "fool" it by "entering" the page using many proxies.
I could use something like Tor, but that's too much work - I only want to visit a page, let the counter or whatever in the page know that I visited, and that's all.
I don't really know which tags to add, but I had some little experiments with Perl so I think that could be a good direction, although I couldn't find a solution for my problem.
Thank you in advance.
You want something like this:
#/usr/bin/perl
use strict; use warnings;
use LWP::UserAgent;
my $url = shift || 'http://www.google.com';
my $a = LWP::UserAgent->new;
$a->agent('Mozilla/5.0');
$a->timeout(20);
while (<DATA>) {
$a->proxy( ['http'], $_ );
warn "Failed to get page with proxy $_\n"
unless $a->get( $url )->is_success;
}
__DATA__
http://85.214.142.3:8080
http://109.230.245.167:80
http://211.222.204.1:80
The code doesn't require much explanations. LWP::UserAgent allows specifying a proxy server.
Loop through a list of proxies, get the wanted page and you're done.

WWW:Mechanize Form Select

I am attempting to login to Youtube with WWW:Mechanize and use forms() to print out all the forms on the page after logging in. My script is logging in successfully, and also successfully navigating to Youtube.com/inbox; However, for some reason Mechanize can not see any forms at Youtube.com/inbox. It just returns blank. Here is my code:
#!"C:\Perl64\bin\perl.exe" -T
use strict;
use warnings;
use CGI;
use CGI::Carp qw/fatalsToBrowser/;
use WWW::Mechanize;
use Data::Dumper;
my $q = CGI->new;
$q->header();
my $url = 'https://www.google.com/accounts/ServiceLogin?uilel=3&service=youtube&passive=true&continue=http://www.youtube.com/signin%3Faction_handle_signin%3Dtrue%26nomobiletemp%3D1%26hl%3Den_US%26next%3D%252Findex&hl=en_US&ltmpl=sso';
my $mechanize = WWW::Mechanize->new(autocheck => 1);
$mechanize->agent_alias( 'Windows Mozilla' );
$mechanize->get($url);
$mechanize->submit_form(
form_id => 'gaia_loginform',
fields => { Email => 'myemail',Passwd => 'mypassword' },
);
die unless ($mechanize->success);
$url = 'http://www.youtube.com/inbox';
$mechanize->get($url);
$mechanize->form_id('comeposeform');
my $page = $mechanize->content();
print Dumper($mechanize->forms());
Mechanize is unable to see any forms at youtube.com/inbox, however, like I said, I can print all of the forms from the initial link, no matter what I change it to...
Thanks in advance.
As always, one of the best debugging approaches is to print what you get and check if it is what you were expecting. This applies to your problem too.
In your case, if you print $mechanize->content() you'll see that you didn't get the page you're expecting. YouTube wants you to follow a JavaScript redirect in order to complete your cross-domain login action. You have multiple options here:
parse the returned content manually – i.e. /location\.replace\("(.+?)"/
try to have your code parse JavaScript (have a look at WWW::Scripter)
[recommended] use YouTube API for managing your inbox