Basraah a33c8c14ee Grant service access by permissions (#692)
* Add service access permissions and migration

`ENABLE_AUTH_<servicename> = True` will have the new permission applied
to the settings configured `DEFAULT_AUTH_GROUP` group or `Member` if
none is configured.

`ENABLE_BLUE_<servicename> = True` will have the new permission applied
to the settings configured `DEFAULT_BLUE_GROUP` group or `Blue` if none
is configured.

* Move views and hooks to permissions based access

* Remove access restriction to services view

Hypothetically non-member/blues could be granted permission to access
services manually as desired now. A user that has no permissions to
access any services will see a blank services list.

* Remove obsolete service settings

* Remove references to obsolete settings

* Adjusted tests to support permissions based access

* Fix incorrectly named permissions

* Add simple get_services generator function

* Added signals for user and groups perm changes

* Update validate_services to support permissions

deactivate_services removed as its surplus to requirements.

* Removed state parameter from validate_services calls

* Update tests to support signals changes

* Fix incorrect call to validate_services task

* Fix validate_services and test

* Add validate_user to changed user groups signal

* Added tests for new signals

* Remove unnecessary post_add signals

* Added documentation for service permissions

* Added detection for members with service active

If there are any service users in the Member or Blue groups active, then
the permission will be added to the respective Member or Blue group.
This means its no longer necessary to maintain the service enablesettings to migrate to permissions based service.

Remove obsolete state based status checking
2017-02-11 22:51:30 -05:00

116 lines
3.7 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 authentication.states import MEMBER_STATE, BLUE_STATE, NONE_STATE
from eveonline.models import EveCharacter
from eveonline.managers import EveManager
from authentication.models import AuthServicesInfo
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
auth = AuthServicesInfo.objects.get(user=request.user)
if not request.user.has_perm(ACCESS_PERM):
messages.error(request, 'You are not authorized to access Discourse.')
return redirect('auth_dashboard')
if not auth.main_char_id:
messages.error(request, "You must have a main character set to access Discourse.")
return redirect('auth_characters')
main_char = EveManager.get_main_character(request.user)
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 auth.main_char_id:
params['avatar_url'] = 'https://image.eveonline.com/Character/%s_256.jpg' % auth.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()
DiscourseTasks.update_groups.apply_async(args=[request.user.pk], countdown=30) # wait 30s for new user creation on Discourse
## Redirect back to Discourse
url = '%s/session/sso_login' % settings.DISCOURSE_URL
return redirect('%s?%s' % (url, query_string))