Implementing custom Curl authentication in Perl script - perl

I am building bot application trying to use perl script to implement the curl request, the issue I have is with Authorization.
The simple curl command is something like.
curl \
-H 'Authorization: Bearer VM2CKBMXI3AVX2GMYPLBMYFRW3RCHYXS' \
'https://api.wit.ai/message?v=20160722&q='
I preferred not to use system() calls from the Perl script as there will be plenty of back and forth between the user and the bot.
I found this library
http://search.cpan.org/~szbalint/WWW-Curl-4.17/lib/WWW/Curl.pm
I was searching for setopt function in order to find out which params does it accept as my issue is where to put the Authorization param inside the command.
I found this link
http://web.mit.edu/darwin/src/modules/curl/curl/perl/Curl_easy/easy.pm
My Script code for now it is like the following:
use strict;
use warnings;
use WWW::Curl::Easy;
my $curl = WWW::Curl::Easy->new;
my $Authorization="Authorization: Bearer VM2CKBMXI3AVX2GMYPLBMYFRW3RCHYXS";
$curl->setopt(CURLOPT_HEADER,1);
$curl->setopt(CURLOPT_URL, 'https://api.wit.ai/message?v=20160721&q=hello');
# A filehandle, reference to a scalar or reference to a typeglob can be used here.
my $response_body;
$curl->setopt(CURLOPT_WRITEDATA,\$response_body);
# Starts the actual request
my $retcode = $curl->perform;
# Looking at the results...
if ($retcode == 0) {
print("Transfer went ok\n");
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
# judge result and next action based on $response_code
print("Received response: $response_body\n");
} else {
# Error code, type of error, error message
print("An error happened: $retcode ".$curl->strerror($retcode)." ".$curl->errbuf."\n");
}
I just need to know which CURLOPT should I need to use in order to implement authorization.
If you have any idea that will be great.
Thanks
Eran Gross

If you just want to set the header "Authorization" the option is CURLOPT_HTTPHEADER:
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Curl::Easy;
my $curl = WWW::Curl::Easy->new;
$curl->setopt(CURLOPT_URL, 'http://127.0.0.1/dumprequest.php');
my #headers = ("Authorization: keygoeshere", "X-Foo: Bah");
$curl->setopt(CURLOPT_HTTPHEADER, \#headers);
$curl->setopt(CURLOPT_HEADER, 1);
my $retcode = $curl->perform;
Gives:
GET dumprequest.php HTTP/1.1
Host: 127.0.0.1
Accept: */*
Authorization: keygoeshere
X-Foo: Bah
But if you actually want to do HTTP authentication then you would use CURLOPT_USERPWD and CURLOPT_HTTPAUTH. See https://curl.haxx.se/libcurl/c/curl_easy_setopt.html for more info on options.

Related

Perl version of this python servicenow script posting a file

So I am going over the Attachment API for ServiceNow, documented here:
https://docs.servicenow.com/integrate/inbound_rest/reference/r_AttachmentAPI-POST.html
For an application I'm working on, I need to write up some Perl code to handle attachments. Unfortunately, the ServiceNow Perl API libraries do not handle attachments larger than 5mb, so I need to use the stock Attachment API that comes with the instance.
From the above link, I saw this example on how to post files with this python code.
#Need to install requests package for python
#easy_install requests
import requests
# Set the request parameters
url = 'https://instance.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=d71f7935c0a8016700802b64c67c11c6&file_name=Issue_screenshot.jpg'
# Specify the file To send. When specifying fles to send make sure you specify the path to the file, in
# this example the file was located in the same directory as the python script being executed.
data = open('Issue_screenshot.jpg', 'rb').read()
# Eg. User name="admin", Password="admin" for this code sample.
user = 'admin'
pwd = 'admin'
# Set proper headers
headers = {"Content-Type":"image/jpeg","Accept":"application/json"}
# Do the HTTP request
response = requests.post(url, auth=(user, pwd), headers=headers, data=data)
# Check for HTTP codes other than 201
if response.status_code != 201:
print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:',response.json())
exit()
# Decode the JSON response into a dictionary and use the data
data = response.json()
print(data)
I've used REST::Client a lot for posting, but unfortunately, I can't find a good example on how to handle the above ^^ but in Perl. How does one use REST::Client to post a file like above?
I've done a temp workaround with this by invoking curl in my scripts, but using REST::Client would be more elegant.
You can use LWP::UserAgent Perl module to achieve the same:
#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;
use Fcntl;
use JSON qw[decode_json];
use Data::Dumper;
my $ua = LWP::UserAgent->new;
my $url = 'https://instance.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=d71f7935c0a8016700802b64c67c11c6&file_name=Issue_screenshot.jpg';
my $user = 'admin';
my $pwd = 'admin';
$ua->credentials( 'instance.service-now.com', '<REALM>', $user, $pwd);
my $file = 'Issue_screenshot.jpg';
my $request = HTTP::Request->new( POST => $url );
$request->header( 'Content-Type' => 'image/jpeg');
$request->header( 'Accept' => 'application/json');
$request->header( 'Content-Length' => -s $file);
sysopen(my $fh,$file,O_RDONLY);
$request->content( sub {
sysread($fh,my $buf,1024);
return $buf;
});
my $res = $ua->request($request);
unless($res->code == 201) {
print 'Status: '.$res->code, 'Headers:',$res->headers_as_string,'Error Response:',$res->content;
exit;
}
my $data = decode_json($res->content);
print Dumper($data);

Using KeyForge API with Perl

I'm trying to call the KeyForge API with a simple Perl program but it doesn't work. I'm using what's in the LWP::UserAgent documentation:
use strict;
use warnings;
use LWP::UserAgent ();
my $ua = LWP::UserAgent->new;
my $response = $ua->get('https://www.keyforgegame.com/api/decks/');
if ($response->is_success) {
print $response->decoded_content;
}
else {
die $response->status_line;
}
The program prints:
500 write failed: at test.pl line 16.
If I use the URL https://www.google.com or http://www.example.com, it works. The HTML is correctly displayed.
If I use this simple PowerShell program, it works too:
$Url = "https://www.keyforgegame.com/api/decks/"
$decks = Invoke-RestMethod ($url)
$decks
It displays:
count data
743719 {#{name=Dr. "The Old" Jeffries; expansion=341; power_level=0; chains=0; wins=0; losses=0; id=ec86db52-e41e-4e...
What am I missing?
PS: I'm using Perl 5.16.3 on Windows 10.
EDIT:
Thank you all for your help. I finally found out what was happening. It turns out I had a very old version of Net::HTTP (from 2013). I upgraded it and now it works out of the box, without configuring agent, cookies or e-mail. The error message I had was actually from the client and not from the server.
$ perl -MLWP::UserAgent -e'
my $ua = LWP::UserAgent->new();
my $response = $ua->get("https://www.keyforgegame.com/api/decks/");
print $response->as_string;
'
HTTP/1.1 403 Forbidden
...
Content-Type: text/html; charset=UTF-8
...
<!DOCTYPE html>
...
<title>Access denied | www.keyforgegame.com used Cloudflare to restrict access</title>
...
<h2 data-translate="what_happened">What happened?</h2>
<p>The owner of this website (www.keyforgegame.com) has banned your access based on your browser's signature (4bfe0c0e2e86ab84-ua22).</p>
...
But,
$ perl -MLWP::UserAgent -e'
use version; our $VERSION = qv("v1.0.0");
my $ua = LWP::UserAgent->new(
agent => "NameOfTool/$VERSION",
from => q{me#example.com},
);
my $response = $ua->get("https://www.keyforgegame.com/api/decks/");
print $response->as_string;
'
HTTP/1.1 200 OK
...
Content-Type: application/json
...
{"count":...
If they want to block you, they can. So it's your best interest to provide a unique application name, a proper version and a valid email address (even if providing junk for the agent and leaving out from field works). This gives them more options to resolve any issues they have with your program.

How to get http and https return code in perl

I am new to perl and any help will be appreciated!!
I have to invoke some URLs through perl (On unix machine).URLs are both http and https
if URL gets invoked successfully,then its fine else create a log file stating that unable to invoke a URL.
For invoking the URL,I am thinking to use for e.g.
exec 'firefox http://www.yahoo.com';
But how to get http and https request status code? Something like if status is 200,then ok else error..
Kindly help!!
Rather than using a browser such a Firefox you should use an HTTP client library such as HTTP::Tiny or LWP::UserAgent.
For exmaple:
#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use HTTP::Tiny;
my $Client = HTTP::Tiny->new();
my #urls = (
'http://www.yahoo.com',
'https://www.google.com',
'http://nosuchsiteexists.com',
);
for my $url (#urls) {
my $response = $Client->get($url);
say $url, ": ", $response->{status};
}
Which outputs:
alex#yuzu:~$ ./return_status.pl
http://www.yahoo.com: 200
https://www.google.com: 200
http://nosuchsiteexists.com: 599
If you want to correctly recognise redirect status codes (3XX) you would have to set the max_redirect parameter to 0.
alex#yuzu:~$ perl -MHTTP::Tiny -E 'say HTTP::Tiny->new(max_redirect => 0)->get("http://www.nestoria.co.uk/soho")->{status};'
301
If all you care about is success then the response hashref contains a 'success' field which will be true on success and false on failure.
alex#yuzu:~$ perl -MHTTP::Tiny -E 'say HTTP::Tiny->new()->get("http://www.google.com")->{success};'
1

Perl ssl client auth using certificate

I'm having trouble getting the following code to work and at a point where I am stuck. I am trying to perform client side authentication using a certificate during a POST request. I'm only interested in sending the client cert to the server and don't really need to check the server certificate.
Here is the cUrl command that trying to replicate:
curl --cacert caCertificate.pem --cert clientCerticate.pem -d "string" https://xx.xx.xx.xx:8443/postRf
I keep getting the following error in my Perl script:
ssl handshake failure
I guess I have two questions: what should I be pointing to for CRT7 AND KEY8 variables? and is this the best way to send a POST request using client cert authentication?
!/usr/bin/perl
use warnings;
use strict;
use Net::SSLeay qw(post_https);
my $$hostIp = "xx.xx.xx.xx"
my $hostPort = "8443"
my $postCommand = "/postRf/string";
my $http_method = 'plain/text';
my $path_to_crt7 = 'pathToCert.pem';
my $path_to_key8 = 'pathToKey.pem';
my ($page, $response, %reply_headers) =
post_https($hostIp, $hostPort, $postCommand, '',
$http_method, $path_to_crt7, $path_to_key8
);
print $page . "\n";
print $response . "\n";
See LWP::UserAgent and IO::Socket::SSL.
use strictures;
use LWP::UserAgent qw();
require LWP::Protocol::https;
my $ua = LWP::UserAgent->new;
$ua->ssl_opts(
SSL_ca_file => 'caCertificate.pem',
SSL_cert_file => 'clientCerticate.pem',
);
$ua->post(
'https://xx.xx.xx.xx:8443/postRf',
Content => 'string',
);
I haven't tested this code.

How do I access the HTTP Header of request in a CGI script?

I've used Perl a bit for small applications and test code, but I'm new to networking and CGI.
I get how to make the header of a request (using CGI.pm and printing the results of the header() function), but haven't been able to find any info on how to access the headers being sent to my CGI script. Could someone point me in the right direction?
This could be from a request like this:
curl http://127.0.0.1:80/cgi-bin/headers.cgi -H "HeaderAttribute: value"
The CGI module has a http() function you can use to that purpose:
#!/usr/bin/perl --
use strict;
use warnings;
use CGI;
my $q = CGI->new;
my %headers = map { $_ => $q->http($_) } $q->http();
print $q->header('text/plain');
print "Got the following headers:\n";
for my $header ( keys %headers ) {
print "$header: $headers{$header}\n";
}
Try it out; the above gives me:
$ curl http://localhost/test.cgi -H "HeaderAttribute: value"
Got the following headers:
HTTP_HEADERATTRIBUTE: value
HTTP_ACCEPT: */*
HTTP_HOST: localhost
HTTP_USER_AGENT: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18
In addition to the CGI.pm http() method you can get HTTP headers information from the environment variables.
So in case you are using something like CGI::Minimal, which doesn't have the http method. you can do something like:
my $header = 'HTTP_X_REQUESTED_WITH';
if (exists $ENV{$header} && lc $ENV{$header} eq 'xmlhttprequest') {
_do_some_ajaxian_stuff();
}
They're supplied as environment variables, such as
HTTP_HEADERATTRIBUTE=value
You may have to do something to configure your web server to supply such a variable, though.