Skip to content

Instantly share code, notes, and snippets.

@pauloborges
Last active December 10, 2015 18:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pauloborges/4476373 to your computer and use it in GitHub Desktop.
Save pauloborges/4476373 to your computer and use it in GitHub Desktop.
@facebook_required decorator
#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
#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