From 8e8fb8e53f80cfddd15b510e1d06eacab7530726 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Thu, 21 Sep 2017 18:01:35 -0400 Subject: [PATCH 1/7] Added OAuth options to the config --- EpisodesCommunity/settings.py | 7 +++++++ options_example.ini | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/EpisodesCommunity/settings.py b/EpisodesCommunity/settings.py index 548e7ef..01e0fac 100644 --- a/EpisodesCommunity/settings.py +++ b/EpisodesCommunity/settings.py @@ -14,10 +14,12 @@ import dj_database_url import os import configparser import warnings +import base64 config = configparser.ConfigParser() config.read('options.ini') options = config['General'] +oauth_options = config['OAuth'] # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -128,3 +130,8 @@ USE_TZ = True # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' + + +AUTH_TOKEN_ENDPOINT = oauth_options.get('token_endpoint','https://icynet.eu/oauth/') +AUTH_B64 = base64.b64encode(bytearray('%s:%s'%(oauth_options.get('client_id'),oauth_options.get('client_secret')),'utf-8')) +AUTH_REDIRECT_URL = oauth_options.get('redirect_url') diff --git a/options_example.ini b/options_example.ini index 9d612ba..516fc94 100644 --- a/options_example.ini +++ b/options_example.ini @@ -9,3 +9,14 @@ secret_key=5up3r s3cr3t k3y #For configuration details database=sqlite:///database.sqlite3 +[OAuth] +#The root of the oauth endpoint you are using for oauth settings +token_endpoint=https://icynet.eu/oauth/ + +#The client id, client secret, and redirect url used in the confguration +#of your oauth client on the site that you plan to use for oauth. +#The redirect url should probably point to the appropriate view, and +#needs to be fully qualified. +client_id=CLIENT ID HERE +client_secret=CLIENT SECRET HERE +redirect_url=REDIRECT URL HERE From cb296bd5650d17950b24d724f0d8842c27bf7838 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 15:58:10 -0400 Subject: [PATCH 2/7] =?UTF-8?q?Fixed=20OAuth=20options=20in=20settings=20?= =?UTF-8?q?=E2=80=94=20Auth=20header=20in=20str=20not=20bytes=20and=20clie?= =?UTF-8?q?nt=20id=20seperated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- EpisodesCommunity/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EpisodesCommunity/settings.py b/EpisodesCommunity/settings.py index 01e0fac..56f9aa1 100644 --- a/EpisodesCommunity/settings.py +++ b/EpisodesCommunity/settings.py @@ -133,5 +133,6 @@ STATIC_URL = '/static/' AUTH_TOKEN_ENDPOINT = oauth_options.get('token_endpoint','https://icynet.eu/oauth/') -AUTH_B64 = base64.b64encode(bytearray('%s:%s'%(oauth_options.get('client_id'),oauth_options.get('client_secret')),'utf-8')) +AUTH_CLIENT_ID = oauth_options.get('client_id') +AUTH_B64 = base64.b64encode(bytearray('%s:%s'%(AUTH_CLIENT_ID,oauth_options.get('client_secret')),'utf-8')).decode("utf-8") AUTH_REDIRECT_URL = oauth_options.get('redirect_url') From e0ef46ca913324cec25e235cdc24d1163d57a241 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 16:00:24 -0400 Subject: [PATCH 3/7] Added login enpoints /login and /login/redirect --- EpisodesCommunity/urls.py | 3 ++- LandingPage/urls.py | 9 +++++++ LandingPage/views.py | 52 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 LandingPage/urls.py diff --git a/EpisodesCommunity/urls.py b/EpisodesCommunity/urls.py index 5068add..b24ce88 100644 --- a/EpisodesCommunity/urls.py +++ b/EpisodesCommunity/urls.py @@ -13,9 +13,10 @@ Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), + url(r'^', include('LandingPage.urls')) ] diff --git a/LandingPage/urls.py b/LandingPage/urls.py new file mode 100644 index 0000000..ae09a29 --- /dev/null +++ b/LandingPage/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + url(r'^login/redirect$', views.LoginRedirect.as_view()), + url(r'^login$', views.Login.as_view()), +] + diff --git a/LandingPage/views.py b/LandingPage/views.py index 91ea44a..e1f5ed1 100644 --- a/LandingPage/views.py +++ b/LandingPage/views.py @@ -1,3 +1,55 @@ from django.shortcuts import render +from django.views import View +from django.conf import settings +from django.http import HttpResponse +from django.http import HttpResponseRedirect +import requests +import hashlib +import json # Create your views here. +# Redirect url should point to this view +class LoginRedirect(View): + def get(self, req): + + # Check state + userstate = generateState(req) + if userstate == req.GET['state']: + code = req.GET['code'] + resp = requests.post( + settings.AUTH_TOKEN_ENDPOINT+"token", + data={ + 'grant_type':'authorization_code', + 'code':code, + 'redirect_uri':settings.AUTH_REDIRECT_URL, + 'client_id':settings.AUTH_CLIENT_ID + }, + headers = { + 'Authorization':'Basic %s'%settings.AUTH_B64 + } + ) + print((settings.AUTH_B64)) + resp_json = resp.json() + if 'error' in resp_json: + return HttpResponse('

OAuth Error

%s
'%json.dumps(resp_json)) + else: + req.session['token'] = resp_json['access_token'] + return HttpResponseRedirect('/') + else: + return HttpResponse('

Unmatching state tokens


It looks like the request to login wasn\'t started by you. Try going back to the home page and logging in again.

', status=400) + +class Login(View): + def get(self, req): + url = '%sauthorize?response_type=code&client_id=%s&redirect_uri=%s&scope=email&state=%s'%(settings.AUTH_TOKEN_ENDPOINT,settings.AUTH_CLIENT_ID,settings.AUTH_REDIRECT_URL, generateState(req)) + response = HttpResponse("Redirecting you to the IcyNet auth page...") + response.status_code = 302 + response['Location'] = url + return response + +def generateState(request): + request.session.save() + + m = hashlib.sha256() + m.update(bytearray(request.session.session_key, 'utf-8')) + m.update(bytearray(settings.SECRET_KEY, 'utf-8')) + return m.hexdigest() From ad5552c37a72ea47aa89b791b7372e5008b201c5 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 16:04:13 -0400 Subject: [PATCH 4/7] Cleaned up debug code --- LandingPage/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/LandingPage/views.py b/LandingPage/views.py index e1f5ed1..8722d81 100644 --- a/LandingPage/views.py +++ b/LandingPage/views.py @@ -28,7 +28,6 @@ class LoginRedirect(View): 'Authorization':'Basic %s'%settings.AUTH_B64 } ) - print((settings.AUTH_B64)) resp_json = resp.json() if 'error' in resp_json: return HttpResponse('

OAuth Error

%s
'%json.dumps(resp_json)) From 49e508f4ad8fbf6e342fc1ad42afab275d435e40 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 16:13:45 -0400 Subject: [PATCH 5/7] Elaborated on error messages for the OAuth redirect endpoint --- LandingPage/views.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/LandingPage/views.py b/LandingPage/views.py index 8722d81..fd1a977 100644 --- a/LandingPage/views.py +++ b/LandingPage/views.py @@ -12,6 +12,13 @@ import json class LoginRedirect(View): def get(self, req): + # Check request has correct arguments + request_valid = 'state' in req.GET and 'code' in req.GET + if not request_valid: + r = HttpResponse('

Error

There was an error in your request. Please try again

') + r.status = 400 + return r + # Check state userstate = generateState(req) if userstate == req.GET['state']: @@ -30,7 +37,9 @@ class LoginRedirect(View): ) resp_json = resp.json() if 'error' in resp_json: - return HttpResponse('

OAuth Error

%s
'%json.dumps(resp_json)) + r = HttpResponse('

OAuth Error

%s
'%json.dumps(resp_json)) + r.status = 500 + return r else: req.session['token'] = resp_json['access_token'] return HttpResponseRedirect('/') From ba0d572cb1e55857923dc24bd6bca09638b67ca2 Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 17:45:17 -0400 Subject: [PATCH 6/7] Updated User model to match changes in IcyNet (User id [int] --> UUIDv4 [varchar]) --- LandingPage/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/LandingPage/models.py b/LandingPage/models.py index c78d9e0..212a8f4 100644 --- a/LandingPage/models.py +++ b/LandingPage/models.py @@ -70,8 +70,9 @@ class Show(TimestampedModel): ) class User(TimestampedModel): - user_id = models.IntegerField( - help_text='The user id assigned to this user by IcyNet\'s auth servers' + user_id = models.CharField( + max_length=36, + help_text='The UUID assigned to this user by IcyNet\'s auth servers' ) email = models.EmailField( help_text='This user\'s email address' From 327052322a6fabd9c5ee4e5b1540645f8e8c1c1f Mon Sep 17 00:00:00 2001 From: Taizo 'Tsa6' Simpson Date: Fri, 22 Sep 2017 17:48:39 -0400 Subject: [PATCH 7/7] Now loads user data to database and user id to session on login --- LandingPage/views.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/LandingPage/views.py b/LandingPage/views.py index fd1a977..dd4be9f 100644 --- a/LandingPage/views.py +++ b/LandingPage/views.py @@ -6,6 +6,7 @@ from django.http import HttpResponseRedirect import requests import hashlib import json +from .models import User # Create your views here. # Redirect url should point to this view @@ -41,6 +42,21 @@ class LoginRedirect(View): r.status = 500 return r else: + user_info = requests.get( + settings.AUTH_TOKEN_ENDPOINT+"user", + headers = { + 'Authorization': 'Bearer ' + resp_json['access_token'] + } + ).json() + req.session['user_id'] = user_info['uuid'] + matches = User.objects.filter(user_id=user_info['uuid']) + if not len(matches): + user = User( + user_id = user_info['uuid'], + email = user_info['email'], + display_name = user_info['display_name'] + ) + user.save() req.session['token'] = resp_json['access_token'] return HttpResponseRedirect('/') else: