Last active
December 14, 2015 07:29
-
-
Save alecs/5051186 to your computer and use it in GitHub Desktop.
twproxy.pl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
use strict; | |
use warnings; | |
#use 5.008; | |
our $HOSTURI = '#'; | |
our $CONSUMER_KEY = '#'; | |
our $CONSUMER_SECRET = '#'; | |
our $SECRET = '#'; | |
our $CALLBACK_URL = '#'; | |
our $TWITTER_API = 'https://api.twitter.com/1'; | |
our $TWITTER_OAUTH_API = 'https://api.twitter.com'; | |
our $TWITTER_SEARCH_API = 'https://search.twitter.com'; | |
our $DEBUG = 'error'; | |
package Model; | |
use Mojo::ByteStream 'b'; | |
use ORLite { | |
file => 'user.db', | |
cleanup => 'VACUUM', | |
create => sub { | |
my $dbh = shift; | |
$dbh->do( | |
"CREATE TABLE user ( | |
username TEXT NOT NULL UNIQUE PRIMARY KEY, | |
password TEXT NOT NULL, | |
ctime INTEGER NOT NULL, | |
oauth_token TEXT, | |
oauth_token_secret TEXT, | |
user_id TEXT, | |
screen_name TEXT | |
);" | |
); | |
}, # end of create => sub | |
}; | |
sub createUpdateUser { | |
my $class = shift; | |
my ($username, $password, $args) = @_; | |
my $oauth_token = $args->{'oauth_token'} || 'NULL'; | |
my $oauth_token_secret = $args->{'oauth_token_secret'} || 'NULL'; | |
my $user_id = $args->{'user_id'}; | |
my $screen_name = $args->{'screen_name'}; | |
if (not Model::User->count('WHERE username=?', $username)) { | |
Model::User->create( | |
username => $username, | |
password => b($SECRET . $password)->md5_sum, | |
ctime => time(), | |
oauth_token => $oauth_token, | |
oauth_token_secret => $oauth_token_secret, | |
user_id => $user_id, | |
screen_name => $screen_name, | |
); | |
} | |
else { | |
$class->do( | |
'UPDATE user | |
SET password = ?, | |
oauth_token = ?, | |
oauth_token_secret = ? | |
WHERE username = ?;', | |
{}, | |
b($SECRET . $password)->md5_sum, | |
$args->{'oauth_token'}, | |
$args->{'oauth_token_secret'}, | |
$username | |
); | |
} | |
} | |
sub getUserInfo { | |
my ($class, $username, $password) = @_; | |
my $user; | |
eval '$user = Model::User->load($username)'; | |
return undef if $@; | |
return $user->password eq b($SECRET . $password)->md5_sum | |
? $user | |
: undef; | |
} | |
sub validate { | |
my $class = shift; | |
my ($username, $password, $repassword, $reg) = @_; | |
return 'username must not be characters of a-z, 0-9, A-Z and _ !' | |
unless $username =~ /^[a-zA-Z0-9][a-z0-9A-Z_]/ | |
and (length $username <= 32); | |
return 'password invalid!' unless $password =~ /^\S{6,32}$/; | |
return 're-password invalid!' | |
if (defined $repassword && $repassword !~ /^\S{6,32}$/); | |
return 'passwords don\'t match!' | |
if (defined $repassword) && ($password ne $repassword); | |
return undef; | |
} | |
package OAuth; | |
use Data::Dumper; | |
use Mojo::ByteStream 'b'; | |
use Net::OAuth; | |
use Mojo::UserAgent; | |
$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A; | |
sub makeOAuthRequest { | |
my ($class, $type, %args) = @_; | |
#local $Net::OAuth::SKIP_UTF8_DOUBLE_ENCODE_CHECK = 1 | |
# if exists $args{'extra_params'}; | |
$args{'consumer_key'} = $CONSUMER_KEY; | |
$args{'consumer_secret'} = $CONSUMER_SECRET; | |
$args{'request_method'} ||= 'POST'; | |
$args{'signature_method'} ||= 'HMAC-SHA1'; | |
$args{'callback'} ||= $CALLBACK_URL; | |
$args{'nonce'} ||= b($SECRET . time())->md5_sum; | |
$args{'timestamp'} = time(); | |
my $request = | |
$args{'from_url'} | |
? Net::OAuth->request($type)->from_url($args{'from_url'}, %args) | |
: Net::OAuth->request($type)->new(%args); | |
$request->sign; | |
return $request; | |
} | |
sub requestToken { | |
my $class = shift; | |
my $request = $class->makeOAuthRequest('request token', | |
request_url => "$TWITTER_OAUTH_API/oauth/request_token",); | |
my $client = Mojo::UserAgent->new->inactivity_timeout(10); | |
my $tx = $client->post($request->to_url); | |
if (my $res = $tx->success) { | |
my %tokens = map { split /=/ } (split /&/, $res->body); | |
if ( exists $tokens{'oauth_token'} | |
and exists $tokens{'oauth_token_secret'}) | |
{ | |
return {result => \%tokens}; | |
} | |
} | |
return {error => "requestToken() error" . Dumper $tx }; | |
} | |
sub getAuthToken { | |
my ($class, $tokens) = @_; | |
my $url = | |
"$TWITTER_OAUTH_API/oauth/authorize?oauth_token=" . $tokens->{'oauth_token'}; | |
my $client = Mojo::UserAgent->new->inactivity_timeout(10); | |
my $tx = $client->get($url); | |
if (my $res = $tx->success) { | |
my $html = $res->body; | |
my ($authenticity_token) = $html =~ /\bform_authenticity_token = \W([a-f0-9]{40})\W\;/; | |
return {result => $authenticity_token} if $authenticity_token; | |
} | |
return {error => "getAuthToken() error" . Dumper $tx }; | |
} | |
sub getCallbackUrl { | |
my $class = shift; | |
my ($username, $password, $authenticity_token, | |
$oauth_token, $oauth_token_secret | |
) = @_; | |
my $client = Mojo::UserAgent->new->inactivity_timeout(10); | |
my $tx = $client->post_form( | |
"$TWITTER_OAUTH_API/oauth/authorize", | |
'UTF-8', | |
{ 'session[username_or_email]' => $_[0], | |
'session[password]' => $_[1], | |
'authenticity_token' => $_[2], | |
'oauth_token' => $_[3], | |
} | |
); | |
if (my $res = $tx->success) { | |
my $html = $res->body; | |
my ($callbackUrl) = $html =~ /\brefresh" content="\d;url=([^"\s]+?)">/; | |
return {result => [$oauth_token_secret, $callbackUrl]} | |
if $callbackUrl; | |
} | |
return {error => "getCallbackUrl() error" . Dumper $tx }; | |
} | |
sub getAccessToken { | |
my ($class, $oauth_token_secret, $callbackUrl) = @_; | |
my $request = $class->makeOAuthRequest( | |
'access token', | |
from_url => $callbackUrl, | |
consumer_key => $CONSUMER_KEY, | |
consumer_secret => $CONSUMER_SECRET, | |
request_url => "$TWITTER_OAUTH_API/oauth/access_token", | |
token_secret => $oauth_token_secret, | |
); | |
my $client = Mojo::UserAgent->new->inactivity_timeout(10); | |
my $tx = $client->post($request->to_url); | |
if (my $res = $tx->success) { | |
my %tokens = map { split /=/ } (split /&/, $res->body); | |
return {result => \%tokens} if exists $tokens{'oauth_token_secret'}; | |
} | |
return {error => "getAccessToken() error" . Dumper $tx }; | |
} | |
package main; | |
use Data::Dumper; | |
use Mojolicious::Lite; | |
use Mojo::ByteStream 'b'; | |
use Mojo::UserAgent; | |
use Mojo::Headers; | |
### dispatchers | |
any '/api/(*path)' => sub { | |
my $self = shift; | |
$self->session(expires => 1); | |
#my $ua = $self->req->headers->user_agent; | |
#print "User agent: $ua\n"; | |
warn Dumper $self->req; | |
my $headers = $self->req->headers; | |
# my $clen = $headers->content_length; | |
# my $ctype = $headers->content_type; | |
my $authz = $headers->header('authorization'); | |
# WATCH ! | |
#if (!$authz) | |
#{ | |
# $self->res->code(403); | |
#} | |
# print "Content len:".$clen."\n"; | |
# print "Content type:".$ctype."\n"; | |
# print "Auth:".$authz."\n"; | |
my $authString = $self->req->headers->authorization =~ /^Basic (\S{1,128})$/ | |
if $self->req->headers->authorization; | |
my ($username, $password) = split /:/, b($authString || "")->b64_decode; | |
my $user = Model->getUserInfo($username, $password); | |
# using stash('path') allows us to get the second | |
# part of the path, after api | |
# | |
# using url->path gets the full path. | |
#my $path = $self->req->url->path; | |
my $path = $self->stash('path'); | |
# we can also get user with: | |
# $self->req->url->query->param('user'); | |
my $useree = $self->req->param('user'); | |
my $params = $self->req->params; | |
print "Params:".$params."\n"; | |
print "Path:".$path."\n"; | |
print "User:".$useree."\n"; | |
my $no_auth_request; | |
if ($user && $user->oauth_token) { | |
$no_auth_request = 0; | |
} | |
elsif($path !~ /^\/?(?:search|trend)/) { | |
$self->res->code(401); | |
return $self->render_text("Forbidden!"); | |
} | |
else { $no_auth_request = 1 } | |
my $request_method = $self->req->method; | |
my $query_params = $self->req->query_params->to_string || ""; | |
my $body_params = $self->req->body_params->to_string || ""; | |
my $url = $path =~ /^\/?(?:search|trend)/ ? $TWITTER_SEARCH_API : $TWITTER_API; | |
$url = $query_params ? "$url/$path?$query_params" : "$url/$path"; | |
my %args = map { split /=/ } (split /&/, $body_params); | |
for my $key (keys %args) { | |
$args{$key} = b($args{$key})->url_unescape->decode('UTF-8'); | |
$args{$key} =~ s/\+/ /g; | |
} | |
my $request = OAuth->makeOAuthRequest( | |
'protected resource', | |
request_method => $request_method, | |
request_url => $url, | |
token => $user->oauth_token, | |
token_secret => $user->oauth_token_secret, | |
nonce => b(rand(1) . $username. time() )->md5_sum, | |
extra_params => \%args, | |
) unless $no_auth_request; | |
my $client = Mojo::UserAgent->new->inactivity_timeout(10); | |
my $tx; | |
# warn Dumper \%args; | |
if ($request_method eq 'GET') { | |
$tx = $client->build_tx($request_method => $url); | |
} | |
elsif ($request_method eq 'POST') { | |
$tx = $client->build_form_tx($url, \%args); | |
} | |
$tx->req->headers->header('Authorization' => $request->to_authorization_header) | |
unless $no_auth_request; | |
$self->res->headers->header('Content-type' => 'application/json'); | |
$client->process($tx); | |
warn Dumper $tx->res->body; | |
return $self->render_text($tx->res->body); | |
} => 'api'; | |
get '/' => 'index'; | |
# OAuth process | |
get '/oauth' => sub { | |
my $self = shift; | |
#return | |
# unless $self->helper( basic_auth => realm => $BASIC_AUTH ); | |
my $res = OAuth->requestToken; | |
return $self->render_text($res->{'error'}) | |
if exists $res->{'error'}; | |
my $tokens = $res->{'result'}; | |
$res = OAuth->getAuthToken($tokens); | |
return $self->render_text($res->{'error'}) | |
if exists $res->{'error'}; | |
my $authenticity_token = $res->{'result'}; | |
$self->session( | |
oauth_token => $tokens->{'oauth_token'}, | |
oauth_token_secret => $tokens->{'oauth_token_secret'}, | |
authenticity_token => $authenticity_token, | |
); | |
$self->render('oauth'); | |
} => 'oauth'; | |
post '/oauth' => sub { | |
my $self = shift; | |
#return | |
# unless $self->helper( basic_auth => realm => $BASIC_AUTH ); | |
my $username = $self->session('username') || $self->param('username'); | |
my $password = $self->param('password'); | |
my $error = Model->validate($username, $password, undef, 1); | |
return $self->render_text($error) if $error; | |
my $res = OAuth->getCallbackUrl( | |
$username, $password, | |
$self->session('authenticity_token'), | |
$self->session('oauth_token'), | |
$self->session('oauth_token_secret'), | |
); | |
return $self->render_text($res->{'error'}) | |
if exists $res->{'error'}; | |
$res = OAuth->getAccessToken(@{$res->{'result'}}); | |
return $self->render_text($res->{'error'}) | |
if exists $res->{'error'}; | |
Model->createUpdateUser($username, $password, $res->{'result'}); | |
$self->redirect_to("/echo"); | |
} => 'oauth'; | |
get '/echo' => sub { | |
my $self = shift; | |
$self->session(expires => 1); | |
return $self->render_text("OK! Now set $HOSTURI as your twitter API!"); | |
} => 'echo'; | |
app->secret(b($SECRET . time())->md5_sum); | |
app->log->path("/tmp/platter-$$.log"); | |
app->log->level($DEBUG); | |
app->start; | |
__DATA__ | |
@@ layouts/main.html.ep | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Platter</title> | |
</head> | |
<body> | |
<div id="main"> | |
<%= content %> | |
</div> | |
<div id="footer"> | |
<hr> | |
</div> | |
</body> | |
</html> | |
@@ index.html.ep | |
%layout 'main'; | |
<p>This is another twitter api proxy server written in Perl.</p> | |
@@ oauth.html.ep | |
% layout 'main'; | |
<div id="oauth"> | |
<form id="oauth-form" action="<%= url_for %>" method="POST"> | |
<input type="hidden" name="authenticity_token" value="<%= session 'authenticity_token' %>"> | |
<input type="hidden" name="oauth_token" value="<%= session 'oauth_token' %>"> | |
<label for="username">Twitter Username:</label> | |
<input type="text" id="username" name="username" value="<%= session 'username' %>"><br> | |
<label for="password">Twitter Password:</label> | |
<input type="password" id="password" name="password" value=""><br> | |
<input type="submit" name="oauth" value="Get Twitter OAuth Token!"> | |
</div> | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment