Made it work :)

This commit is contained in:
David Taylor
2016-11-02 21:01:40 +00:00
parent f5bf40bd9b
commit 793f1d4e05
11 changed files with 207 additions and 55 deletions

View File

@@ -79,58 +79,21 @@ AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
# Environ
# DISCOURSE_HOST='http://localhost:4000'
# DISCOURSE_SSO_SECRET='ABCDEFGHIJKLMNOP'
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/rigboard'
SOCIAL_AUTH_LOGIN_URL = '/'
SOCIAL_AUTH_PIPELINE = (
# Get the information we can about the user and return it in a simple
# format to create the user instance later. On some cases the details are
# already part of the auth response from the provider, but sometimes this
# could hit a provider API.
'social.pipeline.social_auth.social_details',
# Get the social uid from whichever service we're authing thru. The uid is
# the unique identifier of the given user in the provider.
'social.pipeline.social_auth.social_uid',
# Verifies that the current auth process is valid within the current
# project, this is were emails and domains whitelists are applied (if
# defined).
'social.pipeline.social_auth.auth_allowed',
# Checks if the current social-account is already associated in the site.
'social.pipeline.social_auth.social_user',
# Make up a username for this person, appends a random string at the end if
# there's any collision.
# 'social.pipeline.user.get_username',
# Send a validation email to the user to verify its email address.
# Disabled by default.
# 'social.pipeline.mail.mail_validation',
# Associates the current social details with another user account with
# a similar email address. Disabled by default.
'social.pipeline.social_auth.associate_by_email',
# Create a user account if we haven't found one yet.
#'social.pipeline.user.create_user',
# Create the record that associated the social account with this user.
#'social.pipeline.social_auth.associate_user',
# Populate the extra_data field in the social record with the values
# specified by settings (and the default ones like access_token, etc).
'social.pipeline.social_auth.load_extra_data',
# Update the user record with any changed info from the auth service.
'social.pipeline.user.user_details',
'social.pipeline.social_auth.social_details', # Load remote details
'social.pipeline.social_auth.social_uid', # Load remote ID
'social.pipeline.social_auth.auth_allowed', # Check not blacklisted
'social.pipeline.social_auth.social_user', # If already associated, login
'RIGS.discourse.pipeline.new_connection', # Choose a user account, much UI
'social.pipeline.social_auth.associate_user', # Associate the social auth with the user
'social.pipeline.user.user_details', # Save any details that changed
)
DISCOURSE_HOST=os.environ.get('DISCOURSE_HOST') if os.environ.get('DISCOURSE_HOST') else 'http://localhost:4000'
DISCOURSE_SSO_SECRET=os.environ.get('DISCOURSE_SSO_SECRET') if os.environ.get('DISCOURSE_SSO_SECRET') else 'ABCDEFGHIJKLMNOP'
REGISTRATION_OPEN = False # Disable built-in django registration - must register using forum
ROOT_URLCONF = 'PyRIGS.urls'
WSGI_APPLICATION = 'PyRIGS.wsgi.application'

View File

@@ -1,7 +1,6 @@
from __future__ import unicode_literals
import os
from social.backends.base import BaseAuth
from social.exceptions import AuthException
from django.conf import settings
from .sso import DiscourseSSO
@@ -20,8 +19,8 @@ class DiscourseAssociation(object):
class DiscourseAuth(BaseAuth):
"""Discourse authentication backend"""
name = 'discourse'
secret = os.environ['DISCOURSE_SSO_SECRET']
host = os.environ['DISCOURSE_HOST']
secret = settings.DISCOURSE_SSO_SECRET
host = settings.DISCOURSE_HOST
EXTRA_DATA = [
('username', 'username'),

View File

@@ -0,0 +1,62 @@
from django.core.urlresolvers import reverse
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.shortcuts import render_to_response
from django.core.exceptions import ValidationError
from social.pipeline.partial import partial
from RIGS.models import Profile
from RIGS import forms
class SocialRegisterForm(forms.ProfileRegistrationFormUniqueEmail):
def __init__(self, *args, **kwargs):
super(SocialRegisterForm, self).__init__(*args, **kwargs)
self.fields.pop('password1')
self.fields.pop('password2')
self.fields.pop('captcha')
self.fields['email'].widget.attrs['readonly'] = True
def clean_email(self):
initial = getattr(self, 'initial', None)
if(initial['email'] != self.cleaned_data['email']):
raise ValidationError("You cannot change the email")
return initial['email']
@partial
def new_connection(backend, details, response, user=None, is_new=False, social=None, request=None, *args, **kwargs):
if social is not None:
return
data = backend.strategy.request_data()
if data.get('UseCurrentAccount') is not None:
return
alreadyLoggedIn = user is not None
context = {
'details': details,
'alreadyLoggedIn': alreadyLoggedIn,
'loggedInUser': user,
}
if not alreadyLoggedIn:
completeUrl = reverse('social:complete', kwargs={'backend': backend.name})
context['login_url'] = "{0}?{1}={2}".format(reverse('login'), REDIRECT_FIELD_NAME, completeUrl)
if data.get('username') is None:
form = SocialRegisterForm(initial=details)
else:
form = SocialRegisterForm(data, initial=details)
if form.is_valid():
new_user = Profile.objects.create_user(**form.cleaned_data)
return {'user': new_user}
context['form'] = form
return render_to_response('RIGS/social-associate.html', context)

View File

@@ -3,6 +3,7 @@ from django import forms
from django.utils import formats
from django.conf import settings
from django.core import serializers
from django.core.exceptions import ValidationError
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
from registration.forms import RegistrationFormUniqueEmail
from captcha.fields import ReCaptchaField
@@ -27,6 +28,16 @@ class ProfileRegistrationFormUniqueEmail(RegistrationFormUniqueEmail):
raise forms.ValidationError("These initials are already in use. Please supply different initials.")
return self.cleaned_data['initials']
def clean_first_name(self):
if self.cleaned_data["first_name"].strip() == '':
raise ValidationError("First name is required.")
return self.cleaned_data["first_name"]
def clean_last_name(self):
if self.cleaned_data["last_name"].strip() == '':
raise ValidationError("Last name is required.")
return self.cleaned_data["last_name"]
# Login form
class PasswordReset(PasswordResetForm):

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

View File

@@ -133,6 +133,20 @@
{% endif %}
</dd>
</dl>
<h4>Linked to {{object.social_auth.count}} Forum Account(s)</h4>
{% if object.social_auth.count > 0 %}
<a href="{% url 'unlink_forum' %}" class="btn btn-danger">
Unlink Forum Account(s) <span class="glyphicon glyphicon-pencil"></span>
</a>
{% else %}
<a href="{% url "social:begin" "discourse" %}" class="btn btn-success">
Link Forum Account <span class="glyphicon glyphicon-pencil"></span>
</a>
{% endif %}
{% endif %}
{% endif %}
</div>

View File

@@ -0,0 +1,55 @@
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Associate{% endblock %}
{% block content %}
<div class="col-sm-10 col-sm-offset-1">
<div class="text-center">
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
</div>
<h2 class="text-center">Welcome <strong>{{details.username}}</strong></h2>
<h4 class="text-center">This is the first time you've visited RIGS with your forum account, so we need a few details to get you set up</h4>
<hr/>
{% if alreadyLoggedIn %}
<h2 class="text-center">You are logged in to RIGS as <strong>{{loggedInUser.username}}</strong></h2>
<div class="col-sm-8 col-sm-offset-2">
<form action="", method="post">{% csrf_token %}
<input type="hidden" name="UseCurrentAccount" value="1"/>
<button type="submit" class="btn btn-lg btn-primary center btn-block">Link Forum account to RIGS Account</button>
</form>
<a class="btn btn-lg btn-warning center btn-block" href="{% url 'logout' %}" role="button">Logout</a>
</div>
{% else %}
<h4 class="text-center"><a class="btn btn-info" href="{{login_url}}" role="button">I already have a RIGS account</a></h4>
{% if form.errors or supplement_form.errors %}
<div class="alert alert-danger">
{{form.errors}}
{{supplement_form.errors}}
</div>
{% endif %}
<div class="col-sm-8 col-sm-offset-2">
<form action="" method="post" class="form-horizontal" role="form">{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="control-label col-sm-4">{{ field.label }}</label>
<div class="controls col-sm-8">
{% render_field field class+="form-control" placeholder=field.label %}
</div>
</div>
{% endfor %}
<p><input type="submit" value="Register" class="btn btn-primary pull-right"></p>
</form>
</div>
{% endif %}
</div>
{% endblock %}

View File

@@ -1,6 +1,7 @@
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from RIGS import models, views, rigboard, finance, ical, versioning, forms
from RIGS.discourse import views as discourseViews
from django.views.generic import RedirectView
from django.views.decorators.clickjacking import xframe_options_exempt
@@ -15,6 +16,7 @@ urlpatterns = patterns('',
url(r'^closemodal/$', views.CloseModal.as_view(), name='closemodal'),
url('^user/login/$', 'RIGS.views.login', name='login'),
url('^user/associate/$', discourseViews.Associate.as_view(), name='associate'),
url('^user/login/embed/$', xframe_options_exempt(views.login_embed), name='login_embed'),
url(r'^user/password_reset/$', 'django.contrib.auth.views.password_reset', {'password_reset_form': forms.PasswordReset}),
@@ -153,6 +155,7 @@ urlpatterns = patterns('',
url(r'^user/edit/$', login_required(views.ProfileUpdateSelf.as_view()),
name='profile_update_self'),
url(r'^user/reset_api_key$', login_required(views.ResetApiKey.as_view(permanent=False)), name='reset_api_key'),
url(r'^user/unlink_forum$', login_required(views.UnlinkForum.as_view(permanent=False)), name='unlink_forum'),
# ICS Calendar - API key authentication
url(r'^ical/(?P<api_pk>\d+)/(?P<api_key>\w+)/rigs.ics$', api_key_required(ical.CalendarICS()), name="ics_calendar"),

View File

@@ -382,3 +382,12 @@ class ResetApiKey(generic.RedirectView):
self.request.user.save()
return reverse_lazy('profile_detail')
class UnlinkForum(generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
for link in self.request.user.social_auth.all():
link.delete()
self.request.user.save()
return reverse_lazy('profile_detail')

View File

@@ -1,4 +1,5 @@
{% extends 'base.html' %}
{% load static from staticfiles %}
{% block title %}Login{% endblock %}
@@ -6,5 +7,40 @@
<div class="text-center">
<h1>R<small>ig</small> I<small>nformation</small> G<small>athering</small> S<small>ystem</small></h1>
</div>
{% include 'registration/loginform.html' %}
<div class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
Login with TEC Forum
</h4>
</div>
<div id="forumLogin">
<div class="panel-body" style="text-align:center;">
<a class="btn btn-default" href="{% url "social:begin" "discourse" %}?next={{request.GET.next}}">
<h4>Login using</h4>
<img src="{% static "imgs/forum-logo.gif" %}" width=200></img>
</a>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
Login with RIGS Credentials
</h4>
</div>
<div class="panel-body">
<div class="panel-body">
{% include 'registration/loginform.html' %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -13,7 +13,7 @@
{% render_field form.password class+="form-control" placeholder=form.password.label %}
</div>
<div class="text-right">
<a href="{% url 'registration_register' %}" class="btn">Register</a>
{# <a href="{% url 'registration_register' %}" class="btn">Register</a> #}
<a href="{% url 'password_reset' %}" class="btn">Forgotten Password</a>
<input type="submit" value="Login" class="btn btn-primary"/>
<input type="hidden" name="next" value="{{ next }}"/>