2017-03-23 22:54:25 -04:00

109 lines
3.5 KiB
Python

from __future__ import unicode_literals
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from .manager import DiscourseManager
from .tasks import DiscourseTasks
from .models import DiscourseUser
import base64
import hmac
import hashlib
try:
from urllib import unquote, urlencode
except ImportError: #py3
from urllib.parse import unquote, urlencode
try:
from urlparse import parse_qs
except ImportError: #py3
from urllib.parse import parse_qs
import logging
logger = logging.getLogger(__name__)
ACCESS_PERM = 'discourse.access_discourse'
@login_required
def discourse_sso(request):
## Check if user has access
if not request.user.has_perm(ACCESS_PERM):
messages.error(request, 'You are not authorized to access Discourse.')
return redirect('auth_dashboard')
if not request.user.profile.main_character:
messages.error(request, "You must have a main character set to access Discourse.")
return redirect('auth_characters')
main_char = request.user.profile.main_character
if main_char is None:
messages.error(request, "Your main character is missing a database model. Please select a new one.")
return redirect('auth_characters')
payload = request.GET.get('sso')
signature = request.GET.get('sig')
if None in [payload, signature]:
messages.error(request, 'No SSO payload or signature. Please contact support if this problem persists.')
return redirect('auth_dashboard')
# Validate the payload
try:
payload = unquote(payload).encode('utf-8')
decoded = base64.decodestring(payload).decode('utf-8')
assert 'nonce' in decoded
assert len(payload) > 0
except AssertionError:
messages.error(request, 'Invalid payload. Please contact support if this problem persists.')
return redirect('auth_dashboard')
key = str(settings.DISCOURSE_SSO_SECRET).encode('utf-8')
h = hmac.new(key, payload, digestmod=hashlib.sha256)
this_signature = h.hexdigest()
if this_signature != signature:
messages.error(request, 'Invalid payload. Please contact support if this problem persists.')
return redirect('auth_dashboard')
## Build the return payload
username = DiscourseManager._sanitize_username(main_char.character_name)
qs = parse_qs(decoded)
params = {
'nonce': qs['nonce'][0],
'email': request.user.email,
'external_id': request.user.pk,
'username': username,
'name': username,
}
if main_char:
params['avatar_url'] = 'https://image.eveonline.com/Character/%s_256.jpg' % main_char.main_char_id
return_payload = base64.encodestring(urlencode(params).encode('utf-8'))
h = hmac.new(key, return_payload, digestmod=hashlib.sha256)
query_string = urlencode({'sso': return_payload, 'sig': h.hexdigest()})
# Record activation and queue group sync
if not DiscourseTasks.has_account(request.user):
discourse_user = DiscourseUser()
discourse_user.user = request.user
discourse_user.enabled = True
discourse_user.save()
# wait 30s for new user creation on Discourse before triggering group sync
DiscourseTasks.update_groups.apply_async(args=[request.user.pk], countdown=30)
# Redirect back to Discourse
url = '%s/session/sso_login' % settings.DISCOURSE_URL
return redirect('%s?%s' % (url, query_string))