From f5603310595f7532476f93f7090422081463581e Mon Sep 17 00:00:00 2001 From: Xavier Guimard Date: Sat, 5 Oct 2013 16:00:10 +0000 Subject: [PATCH] Facebook auth and userDB modules may work fine now --- .../lib/Lemonldap/NG/Portal/AuthFacebook.pm | 87 ++++++++++++++----- .../lib/Lemonldap/NG/Portal/UserDBFacebook.pm | 71 +++++++++++++++ 2 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDBFacebook.pm diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthFacebook.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthFacebook.pm index f6961bde2..8efe24421 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthFacebook.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthFacebook.pm @@ -3,6 +3,9 @@ ##@class # Facebook authentication backend class. +# +# You need to have an application ID and an application secret (take a look at +# https://developers.facebook.com/apps package Lemonldap::NG::Portal::AuthFacebook; use strict; @@ -49,13 +52,15 @@ sub fb { } # Build Net::Facebook::Oauth2 object - $self->{_fb} = Net::Facebook::Oauth2->new( - application_id => '316131503062', - application_secret => 'e3979b1a6fa02f4833505ccc80987ae3', - callback => $ret, - ); + eval { + $self->{_fb} = Net::Facebook::Oauth2->new( + application_id => $self->{facebookAppId}, + application_secret => $self->{facebookAppSecret}, + callback => $ret, + ); + }; unless ( $self->{_fb} ) { - $self->abort('Unable to build Net::Facebook::Oauth2 object'); + $self->abort( 'Unable to build Net::Facebook::Oauth2 object', $@ ); } return $self->{_fb}; } @@ -67,6 +72,9 @@ sub authInit { unless ($initDone) { eval { require Net::Facebook::Oauth2; }; $self->abort( 'Unable to load Net::Facebook::Oauth2', $@ ) if ($@); + foreach my $arg (qw(facebookAppId facebookAppSecret)) { + $self->abort("Parameter $arg is required") unless ( $self->{$arg} ); + } } PE_OK; } @@ -77,29 +85,60 @@ sub authInit { sub extractFormInfo { my $self = shift; -# TODO: replace this -# -# Lemonldap-ng-dev -# App ID: ********** -# App Secret: ************ -# -# Doc : https://developers.facebook.com/tools/explorer -# -# Other TODO: doc must say that AppID => https://developers.facebook.com/apps -# -# Datas: -# http://graph.facebook.com/100000458059472?fields=id,name,first_name,middle_name,last_name,link,username,gender,locale,timezone,email,location,website - # 1. Check Facebook responses if ( my $code = $self->param('code') ) { if ( my $access_token = $self->fb()->get_access_token( code => $code ) ) { $self->{sessionInfo}->{_facebookToken} = $access_token; - # TODO - my $datas = $self->fb->get('https://graph.facebook.com/me',{fields=>'id,username'})->as_hash; - $self->{user} = $datas->{username} || $access_token; - $self->lmLog( 'Good Facebook authentication', 'debug' ); + # Get fields (see https://developers.facebook.com/tools/explorer) + my @fields = ( 'id', 'username' ); + + # Look at wanted fields + if ( $self->{userDB} =~ /^Facebook/ ) { + push @fields, + map { /^(\w+)$/ ? ($1) : () } + values %{ $self->{exportedVars} }; + } + my $datas; + + # When a field is not granted, Facebook returns only an error + # without real explanation. So here we try to reduce query until + # having a valid response + while (@fields) { + $datas = $self->fb->get( + 'https://graph.facebook.com/me', + { fields => join( ',', @fields ) } + )->as_hash; + unless ( ref $datas ) { + $self->lmLog( "Unable to get any Facebook field", 'error' ); + return PE_ERROR; + } + if ( $datas->{error} ) { + my $tmp = pop @fields; + $self->lmLog( +"Unable to get some Facebook fields ($datas->{error}->{message}). Retrying without $tmp", + 'warn' + ); + } + else { + last; + } + } + unless (@fields) { + $self->lmLog( "Unable to get any Facebook field", 'error' ); + return PE_ERROR; + } + + # Look if a field can be used to trace user + unless ( $self->{user} = $datas->{username} ) { + $self->lmLog( 'Unable to get Facebook username', 'warn' ); + unless ( $self->{user} = $datas->{id} ) { + $self->lmLog( 'Unable to get Facebook id', 'error' ); + return PE_ERROR; + } + } + $self->{_facebookDatas} = $datas; # Force redirection to avoid displaying Oauth datas $self->{mustRedirect} = 1; @@ -113,7 +152,7 @@ sub extractFormInfo { # Build Facebook redirection # TODO: use a param to use "publish_stream" or not my $check_url = $self->fb()->get_authorization_url( - scope => [ 'offline_access', 'publish_stream' ], + scope => ['offline_access'], display => 'page', ); print $self->redirect($check_url); diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDBFacebook.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDBFacebook.pm new file mode 100644 index 000000000..a05ca0f08 --- /dev/null +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/UserDBFacebook.pm @@ -0,0 +1,71 @@ +## @file +# UserDB Facebook module + +## @class +# UserDB Facebook module +# +# To know attributes that can be asked, take a look at +# https://developers.facebook.com/tools/explorer +package Lemonldap::NG::Portal::UserDBFacebook; + +use strict; +use Lemonldap::NG::Portal::Simple; + +our $VERSION = '1.0.0'; + +## @apmethod int userDBInit() +# Check if authentication module is Facebook +# @return Lemonldap::NG::Portal error code +sub userDBInit { + my $self = shift; + + unless ( $self->get_module('auth') =~ /^Facebook/ ) { + $self->lmLog( +'UserDBFacebook isn\'t useable unless authentication module is set to Facebook', + 'error' + ); + return PE_ERROR; + } + PE_OK; +} + +## @apmethod int getUser() +# Does nothing +# @return Lemonldap::NG::Portal error code +sub getUser { + PE_OK; +} + +## @apmethod int setSessionInfo() +# Since the job is done by AuthFacebook, here just check that required +# attributes are not null +# @return Lemonldap::NG::Portal error code +sub setSessionInfo { + my $self = shift; + use Data::Dumper; + while ( my ( $k, $v ) = each %{ $self->{exportedVars} } ) { + my $attr = $k; + my $required = ( $attr =~ s/^!// ) ? 1 : 0; + $self->{sessionInfo}->{$attr} = $self->{_facebookDatas}->{$v}; + if ( $required and not( defined $self->{sessionInfo}->{$attr} ) ) { + $self->lmLog( +"Required parameter $v is not provided by Facebook server, aborted", + 'warn' + ); + + $self->{mustRedirect} = 0; + return PE_MISSINGREQATTR; + } + } + PE_OK; +} + +## @apmethod int setGroups() +# Does nothing +# @return Lemonldap::NG::Portal error code +sub setGroups { + PE_OK; +} + +1; +