Permalink
Browse files

Add facebook authentication to protect against duplicate requests.

  • Loading branch information...
1 parent 4529015 commit 524a5e1467022f8b356d0cdc57a26d27cd0fa07f @hippich committed Nov 24, 2011
Showing with 479 additions and 14 deletions.
  1. +8 −0 Makefile.PL
  2. +7 −0 README.md
  3. +15 −0 faucet_sample.conf
  4. +5 −1 lib/Faucet.pm
  5. +126 −11 lib/Faucet/Controller/Root.pm
  6. +43 −0 lib/Faucet/Model/DB.pm
  7. +20 −0 lib/Faucet/Schema.pm
  8. +181 −0 lib/Faucet/Schema/Result/Facebook.pm
  9. +24 −2 root/src/index
  10. +21 −0 root/static/css/styles.css
  11. +21 −0 schema.sql
  12. +8 −0 t/model_DB.t
View
@@ -22,6 +22,14 @@ requires 'Catalyst::Model::Bitcoin';
requires 'Catalyst::Plugin::Cache';
requires 'Cache::FastMmap';
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';
catalyst;
View
@@ -7,6 +7,13 @@
$ perl Makefile.PL
$ make installdeps
+### DB
+
+Install SQLite3 DB and import schema into faucet.db:
+
+ sqlite3 faucet.db < schema.sql
+
+
### Configuration
Copy supplied sample config file faucet_sample.conf to
View
@@ -40,3 +40,18 @@ blockexplorer_url http://blockexplorer.com/ # URL to project's block explorer pa
cache_size 64m
</backend>
</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.
View
@@ -8,11 +8,15 @@ use Catalyst qw/
ConfigLoader
Static::Simple
Cache
+ Authentication
+ Session
+ Session::Store::FastMmap
+ Session::State::Cookie
/;
extends 'Catalyst';
-our $VERSION = '0.01';
+our $VERSION = '0.02';
$VERSION = eval $VERSION;
# Configure the application.
@@ -1,6 +1,7 @@
package Faucet::Controller::Root;
use Moose;
use namespace::autoclean;
+use Facebook::Graph;
BEGIN { extends 'Catalyst::Controller' }
with 'Catalyst::TraitFor::Controller::reCAPTCHA';
@@ -30,33 +31,58 @@ The root page (/)
sub index :Path :Args(0) {
my ( $self, $c ) = @_;
- $c->stash->{open} = 1;
- my $last = $c->cache->get( $c->req->address );
-
- if ( $last && time - $last < $c->config->{timeout} ) {
- $c->stash->{open} = 0;
- }
+ # Check if user already authorized
+ $c->forward('authorization_check');
+ # Get captcha form
$c->forward('captcha_get');
- if ( $c->req->params->{action} eq 'send' && $c->stash->{open} ) {
- if ( $c->req->params->{address} eq '' ) {
+ if ( $c->req->params->{action} eq 'send' && $c->stash->{open} && $c->stash->{authorized} ) {
+
+ my $address = $c->req->params->{address};
+
+ if ( $address eq '' ) {
$c->stash->{address_error} = 1;
}
if ($c->forward('captcha_check')) {
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
$c->model('BitcoinServer')->send_to_address(
- $c->req->params->{address},
+ $address,
$c->config->{coin_amount}
- );
+ ) unless $repeat_request;
# Update cache and statistics.
$c->stash->{success} = 1;
- $c->cache->set( $c->req->address, time );
$c->stash->{open} = 0;
$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 {
@@ -68,6 +94,41 @@ sub index :Path :Args(0) {
$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 {
my ( $self, $c ) = @_;
@@ -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
Standard 404 error page
View
@@ -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;
View
@@ -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;
Oops, something went wrong.

0 comments on commit 524a5e1

Please sign in to comment.