Skip to content

Commit

Permalink
Add facebook authentication to protect against duplicate requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
hippich committed Nov 24, 2011
1 parent 4529015 commit 524a5e1
Show file tree
Hide file tree
Showing 12 changed files with 479 additions and 14 deletions.
8 changes: 8 additions & 0 deletions Makefile.PL
Expand Up @@ -22,6 +22,14 @@ requires 'Catalyst::Model::Bitcoin';
requires 'Catalyst::Plugin::Cache'; requires 'Catalyst::Plugin::Cache';
requires 'Cache::FastMmap'; requires 'Cache::FastMmap';
requires 'Catalyst::TraitFor::Controller::reCAPTCHA'; requires 'Catalyst::TraitFor::Controller::reCAPTCHA';
requires 'Catalyst::Authentication::Credential::Facebook::OAuth2';
requires 'Catalyst::Plugin::Session::State::Cookie';
requires 'Catalyst::Plugin::Session::Store::FastMmap';
requires 'Catalyst::View::TT';
requires 'Catalyst::Plugin::Authentication';
requires 'Catalyst::Model::DBIC::Schema';
requires 'MooseX::NonMoose' => '0.16';
requires 'MooseX::MarkAsMethods' => '0.13';


test_requires 'Test::More' => '0.88'; test_requires 'Test::More' => '0.88';
catalyst; catalyst;
Expand Down
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -7,6 +7,13 @@
$ perl Makefile.PL $ perl Makefile.PL
$ make installdeps $ make installdeps


### DB

Install SQLite3 DB and import schema into faucet.db:

sqlite3 faucet.db < schema.sql


### Configuration ### Configuration


Copy supplied sample config file faucet_sample.conf to Copy supplied sample config file faucet_sample.conf to
Expand Down
15 changes: 15 additions & 0 deletions faucet_sample.conf
Expand Up @@ -40,3 +40,18 @@ blockexplorer_url http://blockexplorer.com/ # URL to project's block explorer pa
cache_size 64m cache_size 64m
</backend> </backend>
</Plugin::Cache> </Plugin::Cache>

# Facebook authentication
# Obtain application_id and application_secret at http://www.facebook.com/developers/
<Plugin::Authentication>
<facebook>
<credentials>
class Facebook::OAuth2
application_id XXXX
application_secret XXXX
</credentials>
</facebook>
</Plugin::Authentication>

facebook_like_url http://faucet.example.com/
facebook_like_message Just got few bitcoins from Bitcoin Faucet. You can get some too.
6 changes: 5 additions & 1 deletion lib/Faucet.pm
Expand Up @@ -8,11 +8,15 @@ use Catalyst qw/
ConfigLoader ConfigLoader
Static::Simple Static::Simple
Cache Cache
Authentication
Session
Session::Store::FastMmap
Session::State::Cookie
/; /;


extends 'Catalyst'; extends 'Catalyst';


our $VERSION = '0.01'; our $VERSION = '0.02';
$VERSION = eval $VERSION; $VERSION = eval $VERSION;


# Configure the application. # Configure the application.
Expand Down
137 changes: 126 additions & 11 deletions lib/Faucet/Controller/Root.pm
@@ -1,6 +1,7 @@
package Faucet::Controller::Root; package Faucet::Controller::Root;
use Moose; use Moose;
use namespace::autoclean; use namespace::autoclean;
use Facebook::Graph;


BEGIN { extends 'Catalyst::Controller' } BEGIN { extends 'Catalyst::Controller' }
with 'Catalyst::TraitFor::Controller::reCAPTCHA'; with 'Catalyst::TraitFor::Controller::reCAPTCHA';
Expand Down Expand Up @@ -30,33 +31,58 @@ The root page (/)
sub index :Path :Args(0) { sub index :Path :Args(0) {
my ( $self, $c ) = @_; my ( $self, $c ) = @_;


$c->stash->{open} = 1; # Check if user already authorized
my $last = $c->cache->get( $c->req->address ); $c->forward('authorization_check');

if ( $last && time - $last < $c->config->{timeout} ) {
$c->stash->{open} = 0;
}


# Get captcha form
$c->forward('captcha_get'); $c->forward('captcha_get');


if ( $c->req->params->{action} eq 'send' && $c->stash->{open} ) { if ( $c->req->params->{action} eq 'send' && $c->stash->{open} && $c->stash->{authorized} ) {
if ( $c->req->params->{address} eq '' ) {
my $address = $c->req->params->{address};

if ( $address eq '' ) {
$c->stash->{address_error} = 1; $c->stash->{address_error} = 1;
} }


if ($c->forward('captcha_check')) { if ($c->forward('captcha_check')) {
if (! $c->stash->{address_error}) { if (! $c->stash->{address_error}) {

my $repeat_request = 0;

# Check if someone with the same IP already requested coins.
if ( $c->model("DB::Facebook")->search({ ip => $c->req->address })->count > 0 ) {
$repeat_request = 'ip address';
}

# Check if someone with the same coins address already been here.
if ( $c->model("DB::Facebook")->search({ address => $address })->count > 0 ) {
$repeat_request = 'coin address';
}

$c->log->debug("Repeat request: ". $repeat_request);

# send coins # send coins
$c->model('BitcoinServer')->send_to_address( $c->model('BitcoinServer')->send_to_address(
$c->req->params->{address}, $address,
$c->config->{coin_amount} $c->config->{coin_amount}
); ) unless $repeat_request;


# Update cache and statistics. # Update cache and statistics.
$c->stash->{success} = 1; $c->stash->{success} = 1;
$c->cache->set( $c->req->address, time );
$c->stash->{open} = 0; $c->stash->{open} = 0;
$c->cache->set('requests', $c->cache->get('requests') + 1); $c->cache->set('requests', $c->cache->get('requests') + 1);

# Update user object in DB
my $user = $c->model('DB::Facebook')->find( $c->user->token );
$user->update({
ip => $c->req->address,
address => $address,
time => time,
});

# Post link into user's facebook timeline
$c->forward('post_facebook_link');
} }
} }
else { else {
Expand All @@ -68,6 +94,41 @@ sub index :Path :Args(0) {
$c->forward('get_balance'); $c->forward('get_balance');
} }


sub authorization_check :Private {
my ( $self, $c ) = @_;

$c->stash->{open} = 1;
$c->stash->{authorized} = 0;

if ($c->user && $c->user->token) {
my $fb_user = $c->forward('fetch_facebook_info');

if ($fb_user) {
$c->stash->{authorized} = 1;

if ( my $user = $c->model('DB::Facebook')->find($c->user->token) ) {
if ( time - $user->time < $c->config->{timeout} ) {
$c->stash->{open} = 0;
}
}
}
}
}


sub post_facebook_link :Private {
my ($self, $c) = @_;

my $fb = Facebook::Graph->new;
$fb->access_token( $c->user->token );

eval {
$fb->add_link
->set_link_uri( $c->config->{facebook_like_url} )
->set_message( $c->config->{facebook_like_message} )
->publish;
};
}


sub get_balance :Private { sub get_balance :Private {
my ( $self, $c ) = @_; my ( $self, $c ) = @_;
Expand All @@ -85,6 +146,60 @@ sub get_balance :Private {
} }
} }



sub authenticate_facebook :Local {
my ( $self, $c ) = @_;

my $user = $c->authenticate({
scope => ['offline_access', 'publish_stream'],
}, 'facebook');

$c->detach unless $user;

$c->forward('fetch_facebook_info');

$c->res->redirect( $c->uri_for('/') );
}


sub fetch_facebook_user_object :Private {
my ( $self, $c ) = @_;

my $fb = Facebook::Graph->new;
$fb->access_token( $c->user->token );

eval {
return $fb->fetch('me');
};
}


sub fetch_facebook_info :Private {
my ( $self, $c ) = @_;

my $fb_user = $c->forward('fetch_facebook_user_object') or return;

my $db_user = $c->model("DB::Facebook")->find_or_create({
token => $c->user->token
});

$db_user->update({
username => $fb_user->{username},
first_name => $fb_user->{first_name},
last_name => $fb_user->{last_name},
hometown => $fb_user->{hometown}->{name},
hometown_id => $fb_user->{hometown}->{id},
id => $fb_user->{id},
gender => $fb_user->{gender},
location => $fb_user->{location}->{name},
location_id => $fb_user->{location}->{id},
link => $fb_user->{link},
timezone => $fb_user->{timezone},
});

return $fb_user;
}

=head2 default =head2 default
Standard 404 error page Standard 404 error page
Expand Down
43 changes: 43 additions & 0 deletions lib/Faucet/Model/DB.pm
@@ -0,0 +1,43 @@
package Faucet::Model::DB;

use strict;
use base 'Catalyst::Model::DBIC::Schema';

__PACKAGE__->config(
schema_class => 'Faucet::Schema',

connect_info => {
dsn => 'dbi:SQLite:faucet.db',
user => '',
password => '',
}
);

=head1 NAME
Faucet::Model::DB - Catalyst DBIC Schema Model
=head1 SYNOPSIS
See L<Faucet>
=head1 DESCRIPTION
L<Catalyst::Model::DBIC::Schema> Model using schema L<Faucet::Schema>
=head1 GENERATED BY
Catalyst::Helper::Model::DBIC::Schema - 0.59
=head1 AUTHOR
pavel
=head1 LICENSE
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut

1;
20 changes: 20 additions & 0 deletions lib/Faucet/Schema.pm
@@ -0,0 +1,20 @@
use utf8;
package Faucet::Schema;

# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE

use Moose;
use MooseX::MarkAsMethods autoclean => 1;
extends 'DBIx::Class::Schema';

__PACKAGE__->load_namespaces;


# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-11-24 09:47:04
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Q+QZLAvmjAf2E8YyrIPveQ


# You can replace this text with custom code or comments, and it will be preserved on regeneration
__PACKAGE__->meta->make_immutable(inline_constructor => 0);
1;

0 comments on commit 524a5e1

Please sign in to comment.