add support to sign in with mozilla persona

This commit is contained in:
j 2013-09-29 13:00:06 +00:00
parent 8f3aa15eb4
commit a04711e94c
8 changed files with 218 additions and 1 deletions

View file

@ -696,6 +696,7 @@
}, },
"menuExtras": [ "menuExtras": [
"user", "user",
//"persona",
//"locale", //"locale",
"reload" "reload"
], ],

88
pandora/user/persona.py Normal file
View file

@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from django.conf import settings
from django.utils.http import same_origin
from django.core.exceptions import ImproperlyConfigured
from django.contrib.auth.models import User
from django.contrib import auth
try:
from django.utils.encoding import smart_bytes
except ImportError:
from django.utils.encoding import smart_str as smart_bytes
from ox.django.shortcuts import json_response
from ox.utils import json
import requests
import models
def signin(request):
data = json.loads(request.POST['data'])
response = json_response({
'errors': {
'email': 'Failed to verify email'
}
})
verification_data = verify(request, data['assertion'])
if verification_data:
email = verification_data['email']
username = data.get('username')
qs = User.objects.filter(email__iexact=email)
if qs.count() == 0:
if not username:
response = json_response({
'errors': {
'username': 'New user, please provide username'
}
})
return response
user = User()
user.email = email
user.username = username
user.save()
else:
user = qs[0]
if user.is_active:
request.session['ui'] = '{}'
#fixme. use custom backend instead?
user.backend = 'django.contrib.auth.backends.ModelBackend'
auth.login(request, user)
user_json = models.init_user(user, request)
response = json_response({
'user': user_json
})
else:
response = json_response({
'errors': {
'email': 'User Disabled'
}
})
return response
def get_audience(request):
try:
audiences = settings.BROWSERID_AUDIENCES
except AttributeError:
raise ImproperlyConfigured('Required setting BROWSERID_AUDIENCES not found!')
protocol = 'https' if request.is_secure() else 'http'
host = '%s://%s' % (protocol, request.get_host())
for audience in audiences:
if same_origin(host, audience):
return audience
raise ImproperlyConfigured('No audience could be found in BROWSERID_AUDIENCES for host `{0}`.'
.format(host))
def verify(request, assertion):
audience = get_audience(request)
data = {'assertion': assertion, 'audience': audience}
resp = requests.post('https://verifier.login.persona.org/verify', data=data, verify=True)
if resp.ok:
verification_data = json.loads(resp.content)
if verification_data['status'] == 'okay':
return verification_data
return None

View file

@ -23,6 +23,7 @@ from item import utils
import models import models
from decorators import capability_required_json from decorators import capability_required_json
import persona
def signin(request): def signin(request):
''' '''
@ -42,7 +43,9 @@ def signin(request):
} }
''' '''
data = json.loads(request.POST['data']) data = json.loads(request.POST['data'])
if 'username' in data and 'password' in data: if 'assertion' in data:
response = persona.signin(request)
elif 'username' in data and 'password' in data:
data['username'] = data['username'].strip() data['username'] = data['username'].strip()
if settings.AUTH_CHECK_USERNAME: if settings.AUTH_CHECK_USERNAME:
qs = User.objects.filter(username__iexact=data['username']) qs = User.objects.filter(username__iexact=data['username'])

View file

@ -13,3 +13,4 @@ django-celery>=2.4.2
gunicorn>=0.14.3 gunicorn>=0.14.3
html5lib html5lib
South South
requests>=2.0.0

View file

@ -30,24 +30,28 @@ pandora.ui.accountDialogOptions = function(action, value) {
var buttons = { var buttons = {
signin: ['signup', 'reset'], signin: ['signup', 'reset'],
signup: ['signin'], signup: ['signin'],
username: [],
reset: ['signin'], reset: ['signin'],
resetAndSignin: [] resetAndSignin: []
}, },
buttonTitle = { buttonTitle = {
signin: 'Sign In', signin: 'Sign In',
signup: 'Sign Up', signup: 'Sign Up',
username: 'Sign Up',
reset: 'Reset Password', reset: 'Reset Password',
resetAndSignin: 'Sign In' resetAndSignin: 'Sign In'
}, },
dialogText = { dialogText = {
signin: Ox._('To sign in to your account, please enter your username and password.'), signin: Ox._('To sign in to your account, please enter your username and password.'),
signup: Ox._('To sign up for an account, please choose a username and password, and enter your e-mail address.'), signup: Ox._('To sign up for an account, please choose a username and password, and enter your e-mail address.'),
username: Ox._('To sign up for an account, please choose a username.'),
reset: Ox._('To reset your password, please enter either your username or your e-mail address.'), reset: Ox._('To reset your password, please enter either your username or your e-mail address.'),
resetAndSignin: Ox._('To sign in to your account, please choose a new password, and enter the code that we have just e-mailed to you.') resetAndSignin: Ox._('To sign in to your account, please choose a new password, and enter the code that we have just e-mailed to you.')
}, },
dialogTitle = { dialogTitle = {
signin: Ox._('Sign In'), signin: Ox._('Sign In'),
signup: Ox._('Sign Up'), signup: Ox._('Sign Up'),
username: Ox._('Sign Up'),
reset: Ox._('Reset Password'), reset: Ox._('Reset Password'),
resetAndSignin: Ox._('Reset Password') resetAndSignin: Ox._('Reset Password')
}; };
@ -132,6 +136,7 @@ pandora.ui.accountForm = function(action, value) {
var items = { var items = {
'signin': ['username', 'password'], 'signin': ['username', 'password'],
'signup': ['newUsername', 'password', 'email'], 'signup': ['newUsername', 'password', 'email'],
'username': ['newUsername'],
'reset': ['usernameOrEmail'], 'reset': ['usernameOrEmail'],
'resetAndSignin': ['oldUsername', 'newPassword', 'code'] 'resetAndSignin': ['oldUsername', 'newPassword', 'code']
}, },

View file

@ -16,6 +16,8 @@ pandora.ui.mainMenu = function() {
return pandora.$ui.localeButton = pandora.ui.localeButton(); return pandora.$ui.localeButton = pandora.ui.localeButton();
} else if (menuExtra == 'reload') { } else if (menuExtra == 'reload') {
return pandora.$ui.loadingIcon = pandora.ui.loadingIcon(); return pandora.$ui.loadingIcon = pandora.ui.loadingIcon();
} else if (menuExtra == 'persona') {
return pandora.$ui.personaButton = pandora.ui.personaButton();
} }
}), }),
id: 'mainMenu', id: 'mainMenu',

115
static/js/persona.js Normal file
View file

@ -0,0 +1,115 @@
'use strict';
pandora.persona = {};
pandora.persona.init = function(callback) {
(!navigator.id ? Ox.getFile : Ox.noop)(
'https://login.persona.org/include.js',
function() {
navigator.id.watch({
loggedInUser: pandora.user.email ? pandora.user.email : null,
onlogin: onlogin,
onlogout: onlogout
});
callback && callback();
}
);
function getUsername(callback) {
pandora.$ui.accountDialog = pandora.ui.accountDialog('username').open();
pandora.$ui.accountForm.bindEvent({
submit: function(data) {
callback(data.values.username);
pandora.$ui.accountDialog.close();
}
});
return pandora.$ui.accountDialog;
}
function onlogin(assertion, username) {
pandora.api.signin({
assertion: assertion,
username: username
}, function(result) {
if (!result.data.errors && result.status.code == 200) {
pandora.signin(result.data);
} else if (!username
&& result.data.errors && result.data.errors.username) {
getUsername(function(username) {
onlogin(assertion, username);
});
} else {
onlogout();
//fixme: show some error
}
});
}
function onlogout() {
pandora.UI.set({page: ''});
pandora.api.signout({}, function(result) {
pandora.signout(result.data);
});
}
};
pandora.persona.signin = function() {
navigator.id.request();
};
pandora.persona.signout = function() {
navigator.id.logout();
};
pandora.ui.personaButton = function() {
var isGuest = pandora.user.level == 'guest',
that = Ox.Element({
tooltip: Ox._(
isGuest ? 'Click to sign with Persona'
: 'Click to open preferences or doubleclick to sign out'
)
})
.css({marginLeft: '3px'})
.bindEvent({
singleclick: function() {
isGuest
? pandora.persona.signin()
: pandora.UI.set({page: 'preferences'});
},
doubleclick: function() {
pandora.UI.set({page: isGuest ? 'signin' : 'signout'});
}
});
pandora.persona.init();
$('<div>')
.addClass('OxLight')
.css({
float: 'left',
marginTop: '2px',
fontSize: '9px'
})
.html(
isGuest ? Ox._('Sign in')
: Ox.encodeHTMLEntities(pandora.user.username)
)
.appendTo(that);
Ox.Button({
style: 'symbol',
title: 'user',
type: 'image'
})
.css({float: 'left'})
.appendTo(that);
return that;
};

View file

@ -92,6 +92,8 @@ if __name__ == "__main__":
run('git', 'checkout', 'stable/1.4.x') run('git', 'checkout', 'stable/1.4.x')
run('git', 'pull') run('git', 'pull')
os.chdir(base) os.chdir(base)
if old < 3666:
run('./bin/pip', 'install', '-r', 'requirements.txt')
else: else:
if len(sys.argv) == 1: if len(sys.argv) == 1: