Authentication - Rewrite auth system to use Django's user model

This commit is contained in:
Evert Prants 2017-11-13 19:38:51 +02:00
parent 325af6929f
commit 5fb6911960
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
6 changed files with 98 additions and 55 deletions

View File

@ -42,6 +42,11 @@ ALLOWED_HOSTS = []
# Application definition # Application definition
AUTHENTICATION_BACKENDS = (
'LandingPage.backends.OAuthBackend',
'django.contrib.auth.backends.ModelBackend',
)
INSTALLED_APPS = [ INSTALLED_APPS = [
'LandingPage.apps.LandingpageConfig', 'LandingPage.apps.LandingpageConfig',
'Show.apps.ShowConfig', 'Show.apps.ShowConfig',

View File

@ -3,7 +3,7 @@ from .models import *
# Register your models here. # Register your models here.
admin.site.register(Show) admin.site.register(Show)
admin.site.register(User) admin.site.register(ExternalUser)
admin.site.register(Admin) admin.site.register(Admin)
admin.site.register(Ban) admin.site.register(Ban)
admin.site.register(ShowModerator) admin.site.register(ShowModerator)

64
LandingPage/backends.py Normal file
View File

@ -0,0 +1,64 @@
import requests
import hashlib
import json
import logging
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from .models import ExternalUser
class OAuthBackend(ModelBackend):
def authenticate(self, code=None):
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
}
)
resp_json = resp.json()
if 'error' in resp_json:
logging.warn('OAuth server returned an error: %s'%json.dumps(resp_json))
else:
user_info = requests.get(
settings.AUTH_TOKEN_ENDPOINT+"user",
headers = {
'Authorization': 'Bearer ' + resp_json['access_token']
}
).json()
usermodel = get_user_model()
matches = usermodel.objects.filter(externaluser__icy_id=user_info['uuid'])
match = None
if not len(matches):
user = usermodel.objects.create_user(
username = user_info['username'],
email = user_info['email'],
)
if 'privilege' in user_info:
priv = user_info['privilege']
user.is_superuser = (priv == 5)
user.is_staff = (priv > 0)
user.save()
user.externaluser = ExternalUser(
user = user,
icy_id = user_info['uuid'],
display_name = user_info['display_name']
)
user.externaluser.save()
match = user
else:
match = matches[0]
match.access_token = resp_json['access_token']
return match
return None

View File

@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User
from django.core.files.storage import FileSystemStorage from django.core.files.storage import FileSystemStorage
from django.conf import settings from django.conf import settings
import os import os
@ -71,14 +72,17 @@ class Show(TimestampedModel):
def __str__(self): def __str__(self):
return '%s [%s]'%(self.name,self.abbr) return '%s [%s]'%(self.name,self.abbr)
class User(TimestampedModel): class ExternalUser(TimestampedModel):
user_id = models.CharField( user = models.OneToOneField(
User,
on_delete=models.CASCADE,
help_text='The internal Django user',
verbose_name="User"
)
icy_id = models.CharField(
max_length=36, max_length=36,
help_text='The UUID assigned to this user by IcyNet\'s auth servers' help_text='The UUID assigned to this user by IcyNet\'s auth servers'
) )
email = models.EmailField(
help_text='This user\'s email address'
)
display_name=models.CharField( display_name=models.CharField(
max_length=20, max_length=20,
help_text="The name shown to other users", help_text="The name shown to other users",
@ -95,7 +99,7 @@ class User(TimestampedModel):
through='Watch' through='Watch'
) )
def __str__(self): def __str__(self):
return self.email return 'External for %s (%s)'%(self.user.email, self.display_name)
class Admin(User): class Admin(User):
pass pass
@ -285,7 +289,7 @@ class Submission(TimestampedModel):
verbose_name='Submitted For' verbose_name='Submitted For'
) )
user = models.ForeignKey( user = models.ForeignKey(
'User', User,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
related_name='submissions', related_name='submissions',
@ -309,7 +313,7 @@ class SubmissionVote(TimestampedModel):
help_text='What this submission was cast on' help_text='What this submission was cast on'
) )
user = models.ForeignKey( user = models.ForeignKey(
'User', User,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='votes', related_name='votes',
help_text='The user who cast this vote' help_text='The user who cast this vote'
@ -322,7 +326,7 @@ class SubmissionVote(TimestampedModel):
class Favorite(TimestampedModel): class Favorite(TimestampedModel):
user = models.ForeignKey( user = models.ForeignKey(
User, ExternalUser,
on_delete=models.CASCADE on_delete=models.CASCADE
) )
episode = models.ForeignKey( episode = models.ForeignKey(
@ -334,7 +338,7 @@ class Favorite(TimestampedModel):
class Watch(TimestampedModel): class Watch(TimestampedModel):
user = models.ForeignKey( user = models.ForeignKey(
User, ExternalUser,
on_delete=models.CASCADE on_delete=models.CASCADE
) )
episode = models.ForeignKey( episode = models.ForeignKey(

View File

@ -20,8 +20,8 @@
<div class="unibar"> <div class="unibar">
<span class="logo">Episodes<span class="period">.</span>Community</span> <span class="logo">Episodes<span class="period">.</span>Community</span>
<div class="userdata"> <div class="userdata">
{% if request.session.user_id %} {% if user.is_authenticated %}
{{ request.session.disp_name }} {{ user.externaluser.display_name }}
{% else %} {% else %}
<a href="/login">Log in</a> <a href="/login">Log in</a>
{% endif %} {% endif %}

View File

@ -1,6 +1,7 @@
from django.shortcuts import render from django.shortcuts import render
from django.views import View from django.views import View
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.contrib.auth import login as auth_login, authenticate
from django.conf import settings from django.conf import settings
from django.http import HttpResponse from django.http import HttpResponse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -29,52 +30,21 @@ class LoginRedirect(View):
userstate = generateState(req) userstate = generateState(req)
if userstate == req.GET['state']: if userstate == req.GET['state']:
code = req.GET['code'] code = req.GET['code']
resp = requests.post(
settings.AUTH_TOKEN_ENDPOINT+"token", user = authenticate(code=code)
data={
'grant_type':'authorization_code', if user is not None and user.is_active:
'code':code, auth_login(req, user)
'redirect_uri':settings.AUTH_REDIRECT_URL,
'client_id':settings.AUTH_CLIENT_ID
},
headers = {
'Authorization':'Basic %s'%settings.AUTH_B64
}
)
resp_json = resp.json()
if 'error' in resp_json:
r = HttpResponse('<h1>OAuth Error</h1><pre>%s</pre>'%json.dumps(resp_json))
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'])
match = None
if not len(matches):
user = User(
user_id = user_info['uuid'],
email = user_info['email'],
display_name = user_info['display_name']
)
user.save()
match = user
else:
match = matches[0]
req.session['token'] = resp_json['access_token']
req.session['disp_name'] = match.display_name
return HttpResponseRedirect('/') return HttpResponseRedirect('/')
else:
return HttpResponse('<h1>Error</h1><br><p>It looks like something went wrong while trying to authenticate you. Please try again later.</p>', status=500)
return HttpResponse('<h1>Unmatching state tokens</h1><br><p>It looks like the request to login wasn\'t started by you. Try going back to the home page and logging in again.</p>', status=400) return HttpResponse('<h1>Unmatching state tokens</h1><br><p>It looks like the request to login wasn\'t started by you. Try going back to the home page and logging in again.</p>', status=400)
class Login(View): class Login(View):
def get(self, req): 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)) url = '%sauthorize?response_type=code&client_id=%s&redirect_uri=%s&scope=email privilege&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 = HttpResponse("Redirecting you to the IcyNet auth page...")
response.status_code = 302 response.status_code = 302
response['Location'] = url response['Location'] = url