Last active
December 10, 2015 18:38
-
-
Save pauloborges/4476373 to your computer and use it in GitHub Desktop.
@facebook_required decorator
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
#coding: utf-8 | |
from functools import wraps | |
from django.conf import settings | |
from django.shortcuts import redirect | |
from django.core.urlresolvers import reverse | |
from django.utils.decorators import available_attrs | |
from django.contrib.auth import REDIRECT_FIELD_NAME | |
from django.contrib.auth.views import redirect_to_login | |
from django.contrib.auth.decorators import login_required | |
from facepy import GraphAPI | |
from social_auth.models import UserSocialAuth | |
from social_auth.backends.facebook import FacebookBackend | |
def build_redirect_url(request, login_url=None): | |
""" | |
Build the current url in 'request' for redirect purposes. | |
If the current url and 'login_url' have the same scheme and location, then | |
the redirect url will be just the path (e.g. "/path/to/somewhere/") and not | |
the full uri (e.g. "http://domain.com/path/to/somewhere/"). | |
Piece of code from 'django.contrib.auth.decorators.login_required' | |
decorator. | |
Source: github.com/django/django/blob/1.4/django/contrib/auth/decorators.py | |
""" | |
import urlparse | |
path = request.build_absolute_uri() | |
# If the login url is the same scheme and net location then just | |
# use the path as the "next" url. | |
login_scheme, login_netloc = urlparse.urlparse(login_url or | |
settings.LOGIN_URL)[:2] | |
current_scheme, current_netloc = urlparse.urlparse(path)[:2] | |
if ((not login_scheme or login_scheme == current_scheme) and | |
(not login_netloc or login_netloc == current_netloc)): | |
path = request.get_full_path() | |
return path | |
def facebook_required(function=None, scope=[], login_url=None, | |
fb_login_url=None): | |
""" | |
Este decorator incorpora duas funcionalidades: | |
1. Verifica se o usuário está logado com a conta atual. Caso não esteja, o | |
redireciona para fazer o login em 'login_url' | |
2. Verifica se o usuário autorizou o app com as permissões extras | |
encontradas em 'scope'. | |
A segunda verificação é mais complexa, pois o usuário pode possuir um | |
'UserSocialAuth' com um 'access_token' expirado ou até mesmo após ter | |
desautorizado o app. | |
Se o usuário não possuir um 'UserSocialAuth', o redirecionamos para | |
'fb_login_url'. | |
Se o usuário possui um 'UserSocialAuth'. Precisamos verificar se o | |
'access_token' é válido. Fazemos isso através de uma requisição a url | |
"https://graph.facebook.com/me/permissions". | |
Caso 'FacebookError' seja levantado (app não autorizado ou token expirado) | |
ou 'scope' não está entre as permissões que foram retornadas, | |
redirecionamos o usuário para 'fb_login_url'. | |
FIXME: Como o deauthorization_callback não está funcionando ainda, quando | |
um usuário desautoriza o aplicativo ele ainda mantém todo o cadastro | |
(inclusive um 'UserSocialAuth' com token inválido). | |
FIXME2: Apesar de verificar 'scope', não leva o 'scope' adiante (o usuário | |
não é requisitado a aceitar as permissões dentro dele, apenas as default | |
que estão no 'settings'). | |
FIXME3: Essa parte de autenticação com o Facebook deve ser realizada | |
idealmente pela API Javascript. Em breve modificar esse recorator de | |
forma que deixe isso possível. | |
IDEIA: talvez a solução seja um template context processor que insira uma | |
variável 'facebook' que, no caso default, é vazio; e quando é necessária | |
alguma autenticação, possui todo o código para carregar a API Javascript | |
e o escambal. | |
""" | |
def decorator(view_func): | |
@wraps(view_func, assigned=available_attrs(view_func)) | |
def _wrapped_view(request, *args, **kwargs): | |
# Primeiro verifica se o usuário está logado com a conta local. | |
# Caso não esteja, o redireciona para logar. | |
user = request.user | |
if not user.is_authenticated(): | |
loginurl = login_url or settings.LOGIN_URL | |
return redirect_to_login(build_redirect_url(request, loginurl), | |
login_url=loginurl) | |
# Verifica se o usuário possui uma Graph API (inserida no 'request' | |
# pelo 'FacebookGraphMiddleware'. Se o usuário não possuir, | |
# significa que ele não possui um 'UserSocialAuth'. | |
if not request.graph: | |
return redirect_to_login(request.build_absolute_uri(), | |
login_url=fb_login_url or settings.FACEBOOK_LOGIN_URL) | |
# Verifica se o access_token é válido e se o usuário possui as | |
# permissões necessárias. | |
try: | |
permissions = request.graph.get('me/permissions')['data'][0] | |
for perm in scope: | |
if perm not in permissions: | |
raise GraphAPI.FacebookError('Permission "%s" not' | |
'found.' % perm) | |
except GraphAPI.FacebookError: | |
return redirect_to_login(request.build_absolute_uri(), | |
login_url=fb_login_url or settings.FACEBOOK_LOGIN_URL) | |
# Caso esteja tudo ok com o usuário, executa a view original. | |
return view_func(request, *args, **kwargs) | |
return _wrapped_view | |
if function: | |
return decorator(function) | |
return decorator |
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
#coding: utf-8 | |
from facepy import GraphAPI | |
from social_auth.models import UserSocialAuth | |
from social_auth.backends.facebook import FacebookBackend | |
class FacebookGraphMiddleware(object): | |
"""TODO""" | |
def process_request(self, request): | |
try: | |
social_user = UserSocialAuth.objects.get(user=request.user.id, | |
provider=FacebookBackend.name) | |
request.graph = GraphAPI(social_user.tokens['access_token']) | |
except: | |
request.graph = None |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment