add support to sign in with mozilla persona
This commit is contained in:
parent
8f3aa15eb4
commit
a04711e94c
8 changed files with 218 additions and 1 deletions
|
@ -696,6 +696,7 @@
|
||||||
},
|
},
|
||||||
"menuExtras": [
|
"menuExtras": [
|
||||||
"user",
|
"user",
|
||||||
|
//"persona",
|
||||||
//"locale",
|
//"locale",
|
||||||
"reload"
|
"reload"
|
||||||
],
|
],
|
||||||
|
|
88
pandora/user/persona.py
Normal file
88
pandora/user/persona.py
Normal 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
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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']
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
115
static/js/persona.js
Normal 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;
|
||||||
|
|
||||||
|
};
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue